ComposeActivity.java revision 154386a287a9bfffb2eb3a4c05445f2774e1d4f0
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;
8075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.HashMap;
8146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.HashSet;
8246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.List;
834a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport java.util.Set;
8475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.Map.Entry;
8582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport java.util.concurrent.ConcurrentHashMap;
8646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
8746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereirapublic class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
885a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        RespondInlineListener, DialogInterface.OnClickListener, TextWatcher,
895a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        AttachmentDeletedListener, OnAccountChangedListener {
906349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Identifiers for which type of composition this is
916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int COMPOSE = -1;  // also used for editing a draft
926349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY = 0;
936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY_ALL = 1;
946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int FORWARD = 2;
956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Integer extra holding one of the above compose action
976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_ACTION = "action";
986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
9982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
10082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Map containing information about requests to create new messages, and the id of the
10182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // messages that were the result of those requests.
10282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // This map is used when the activity that initiated the save a of a new message, is killed
10482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // before the save has completed (and when we know the id of the newly created message).  When
10582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // a save is completed, the service that is running in the background, will update the map
10682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // When a new ComposeActivity instance is created, it will attempt to use the information in
10882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
10982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
11082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // to populate the map with data stored in shared preferences.
11182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
11282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Key used to store the above map
11382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static final String CACHED_MESSAGE_REQUEST_IDS_KEY = "cache-message-request-ids";
1146349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1156349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Notifies the {@code Activity} that the caller is an Email
1166349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * {@code Activity}, so that the back behavior may be modified accordingly.
1176349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     *
1186349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * @see #onAppUpPressed
1196349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1206349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
1216349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1226349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    //  If this is a reply/forward then this extra will hold the original message uri
1236349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE_URI = "in-reference-to-uri";
1244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static final String END_TOKEN = ", ";
125013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final String LOG_TAG = new LogUtils().getLogTag();
126013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    // Request numbers for activities we start
127013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_PICK_ATTACHMENT = 1;
128013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_CREATE_ACCOUNT = 2;
1298e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
13082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
13182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * A single thread for running tasks in the background.
13282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
13382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Handler mSendSaveTaskHandler = null;
134c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mTo;
135c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mCc;
136c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mBcc;
1378e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private Button mCcBccButton;
1388e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private CcBccView mCcBccView;
1397b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira    private AttachmentsView mAttachmentsView;
14033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private Account mAccount;
14182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Rfc822Validator mValidator;
1426349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private Uri mRefMessageUri;
1436349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private TextView mSubject;
1446349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
145326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private ComposeModeAdapter mComposeModeAdapter;
146326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private int mComposeMode = -1;
14746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private boolean mForward;
14846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private String mRecipient;
14946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private QuotedTextView mQuotedTextView;
150eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private TextView mBodyView;
1511a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private View mFromStatic;
152eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private View mFromSpinnerWrapper;
1535a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    private FromAddressSpinner mFromSpinner;
154013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private boolean mAddingAttachment;
155eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private boolean mAttachmentsChanged;
15682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mTextChanged;
15782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mReplyFromChanged;
15882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSave;
15982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSend;
16082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Object mDraftIdLock = new Object();
16182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private long mRefMessageId;
16282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private AlertDialog mRecipientErrorDialog;
163eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private AlertDialog mSendConfirmDialog;
164326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    /**
165326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     * Can be called from a non-UI thread.
166326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     */
16782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public static void editDraft(Context launcher, Account account, long localMessageId) {
168326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
169326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1706349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1716349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1726349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
17333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void compose(Context launcher, Account account) {
1746349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, null, COMPOSE);
1756349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1766349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1776349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1786349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1796349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
18033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void reply(Context launcher, Account account, String uri) {
1816349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, REPLY);
1826349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1836349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1846349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1856349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1866349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
18733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void replyAll(Context launcher, Account account, String uri) {
1886349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, REPLY_ALL);
1896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1906349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1926349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
19433fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void forward(Context launcher, Account account, String uri) {
1956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, FORWARD);
1966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
19833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private static void launch(Context launcher, Account account, String uri, int action) {
1996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Intent intent = new Intent(launcher, ComposeActivity.class);
2006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
2016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_ACTION, action);
2026349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
2036349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI, uri);
2046349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launcher.startActivity(intent);
2056349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2068e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
2078e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
2088e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onCreate(Bundle savedInstanceState) {
2098e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        super.onCreate(savedInstanceState);
2103528d3657bd8f6f128e2b9b18acd018c4b3e5130Mindy Pereira        Intent intent = getIntent();
21133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        mAccount = (Account)intent.getParcelableExtra(Utils.EXTRA_ACCOUNT);
2128e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        setContentView(R.layout.compose);
21346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        findViews();
21446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
21546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
21646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            mRefMessageUri = Uri.parse(intent.getStringExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI));
21733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(action, mAccount.name);
21846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
219eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mQuotedTextView.setVisibility(View.GONE);
22046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
2211a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        initActionBar(action);
2225a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        initFromSpinner();
22375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        initChangeListeners();
2241a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2251a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
2261a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    @Override
2271a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    protected void onResume() {
2281a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        super.onResume();
2291a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // Update the from spinner as other accounts
2301a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // may now be available.
2315a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
2321a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2331a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
234eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
235eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected void onPause() {
236eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onPause();
237eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
238eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mSendConfirmDialog != null) {
239eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mSendConfirmDialog.dismiss();
240eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
241eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mRecipientErrorDialog != null) {
242eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mRecipientErrorDialog.dismiss();
243eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
244eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
245eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeeded();
246eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
247eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
248eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
249eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected final void onActivityResult(int request, int result, Intent data) {
250eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAddingAttachment = false;
251eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
252eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (result == RESULT_OK && request == RESULT_PICK_ATTACHMENT) {
253eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            addAttachmentAndUpdateView(data);
254eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
255eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
256eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
257eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
258eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public final void onSaveInstanceState(Bundle state) {
259eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onSaveInstanceState(state);
260eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
261eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // onSaveInstanceState is only called if the user might come back to this activity so it is
262eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // not an ideal location to save the draft. However, if we have never saved the draft before
263eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // we have to save it here in order to have an id to save in the bundle.
264eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeededOnOrientationChanged();
265eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
266eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
2671a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private void initFromSpinner() {
2685a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.setCurrentAccount(mAccount);
2695a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
270eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        boolean showSpinner = mFromSpinner.getCount() > 1;
271eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // If there is only 1 account, just show that account.
272eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // Otherwise, give the user the ability to choose which account to send
273eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // mail from / save drafts to.
274eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromStatic.setVisibility(
275eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.GONE : View.VISIBLE);
276eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper.setVisibility(
277eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.VISIBLE : View.GONE);
27846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
27946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
28046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void findViews() {
281a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccButton = (Button) findViewById(R.id.add_cc_bcc);
2828e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        if (mCcBccButton != null) {
2838e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira            mCcBccButton.setOnClickListener(this);
2848e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
2858e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
2867b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira        mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
287eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsView.setAttachmentChangesListener(this);
288eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        setupRecipients();
28982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // TODO: add special chips text change watchers before adding
29082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // this as a text changed watcher to the to, cc, bcc fields.
2916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        mSubject = (TextView) findViewById(R.id.subject);
29246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
29346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView.setRespondInlineListener(this);
294eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView = (TextView) findViewById(R.id.body);
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
30075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Now that the message has been initialized from any existing draft or
30175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // ref message data, set up listeners for any changes that occur to the
30275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // message.
30375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private void initChangeListeners() {
30475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mSubject.addTextChangedListener(this);
30575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBodyView.addTextChangedListener(this);
30675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTo.addTextChangedListener(new RecipientTextWatcher(mTo, this));
30775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mCc.addTextChangedListener(new RecipientTextWatcher(mCc, this));
30875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBcc.addTextChangedListener(new RecipientTextWatcher(mBcc, this));
30975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mFromSpinner.setOnAccountChangedListener(this);
31075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
31175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
312326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private void initActionBar(int action) {
313326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mComposeMode = action;
314eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        ActionBar actionBar = getActionBar();
315326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
316eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
317eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(R.string.compose);
318326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else {
319eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(null);
320326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mComposeModeAdapter == null) {
321326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mComposeModeAdapter = new ComposeModeAdapter(this);
322326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
323eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
324eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
325326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            switch (action) {
326326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY:
327eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(0);
328326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
329326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY_ALL:
330eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(1);
331326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
332326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.FORWARD:
333eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(2);
334326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
335326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
3366349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
3376349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
3386349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
33946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initFromRefMessage(int action, String recipientAddress) {
3406349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        ContentResolver resolver = getContentResolver();
3416349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Cursor refMessage = resolver.query(mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
3426349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira                null, null);
3436349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        if (refMessage != null) {
34446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            try {
34546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                refMessage.moveToFirst();
34682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mRefMessageId = refMessage.getLong(UIProvider.MESSAGE_ID_COLUMN);
34746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                setSubject(refMessage, action);
34846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                // Setup recipients
34946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                if (action == FORWARD) {
35046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    mForward = true;
35146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                }
35246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                initRecipientsFromRefMessageCursor(recipientAddress, refMessage, action);
35346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                initBodyFromRefMessage(refMessage, action);
35446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
3557a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira                    initAttachments(refMessage);
35646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                }
35746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                updateHideOrShowCcBcc();
35846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            } finally {
35946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                refMessage.close();
36046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            }
3616349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
362c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    }
363c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira
3647a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira    private void initAttachments(Cursor refMessage) {
3657a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira        mAttachmentsView.addAttachments(mAccount, refMessage);
366eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
367eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
36846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initBodyFromRefMessage(Cursor refMessage, int action) {
3699932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
3709932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
3719932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        }
37246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
37346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
37446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void updateHideOrShowCcBcc() {
375ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        // Its possible there is a menu item OR a button.
376a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean ccVisible = !TextUtils.isEmpty(mCc.getText());
377a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean bccVisible = !TextUtils.isEmpty(mBcc.getText());
378a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (ccVisible || bccVisible) {
379a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            mCcBccView.show(false, ccVisible, bccVisible);
380a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
381ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
382ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!mCc.isShown() || !mBcc.isShown()) {
383ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.VISIBLE);
384ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setText(getString(!mCc.isShown() ? R.string.add_cc_label
385ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
386ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
387ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.GONE);
388ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
389ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
39046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
39146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
392013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    /**
393013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * Add attachment and update the compose area appropriately.
394013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * @param data
395013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     */
396013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void addAttachmentAndUpdateView(Intent data) {
397013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Uri uri = data != null ? data.getData() : null;
398013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        try {
3997aee8f75f07eb39ecacdf0630d2199211aea5718Mindy Pereira            long size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */);
4009932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            if (size > 0) {
4019932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                mAttachmentsChanged = true;
4029932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                updateSaveUi();
403013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            }
4049932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        } catch (AttachmentFailureException e) {
4059932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // A toast has already been shown to the user, no need to do
4069932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // anything.
4079932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            LogUtils.e(LOG_TAG, e, "Error adding attachment");
408013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
409013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
410013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
41146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initRecipientsFromRefMessageCursor(String recipientAddress, Cursor refMessage,
41246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            int action) {
4134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Don't populate the address if this is a forward.
4144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (action == ComposeActivity.FORWARD) {
4154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
4164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
41733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        initReplyRecipients(mAccount.name, refMessage, action);
4184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void initReplyRecipients(String account, Cursor refMessage, int action) {
4214a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // This is the email address of the current user, i.e. the one composing
4224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the reply.
4234a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        final String accountEmail = Address.getEmailAddress(account).getAddress();
4244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String fromAddress = refMessage.getString(UIProvider.MESSAGE_FROM_COLUMN);
4254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String[] sentToAddresses = Utils.splitCommaSeparatedString(refMessage
4264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                .getString(UIProvider.MESSAGE_TO_COLUMN));
4274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String[] replytoAddresses = Utils.splitCommaSeparatedString(refMessage
4284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                .getString(UIProvider.MESSAGE_REPLY_TO_COLUMN));
429a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        final Collection<String> toAddresses;
4304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // If this is a reply, the Cc list is empty. If this is a reply-all, the
4324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Cc list is the union of the To and Cc recipients of the original
4334a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, excluding the current user's email address and any addresses
434a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        // already on the To list.
435a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (action == ComposeActivity.REPLY) {
436a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress,
437a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                    replytoAddresses, new String[0]);
438a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            addToAddresses(toAddresses);
439a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        } else if (action == ComposeActivity.REPLY_ALL) {
4404a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            final Set<String> ccAddresses = Sets.newHashSet();
441a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress,
442a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                    replytoAddresses, new String[0]);
443154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            addToAddresses(toAddresses);
4444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, sentToAddresses);
4454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, Utils.splitCommaSeparatedString(refMessage
4464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    .getString(UIProvider.MESSAGE_CC_COLUMN)));
4474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addCcAddresses(ccAddresses, toAddresses);
4484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4494a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addToAddresses(Collection<String> addresses) {
4524a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addAddressesToList(addresses, mTo);
4534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
4564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addCcAddressesToList(tokenizeAddressList(addresses), tokenizeAddressList(toAddresses),
4574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                mCc);
4584a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4594a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4604a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
4614a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected void addCcAddressesToList(List<Rfc822Token[]> addresses,
4624a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            List<Rfc822Token[]> compareToList, RecipientEditTextView list) {
4634a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String address;
4644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4654a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> compareTo = convertToHashSet(compareToList);
4664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : addresses) {
4674a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4684a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                address = tokens[i].toString();
4694a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Check if this is a duplicate:
4704a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                if (!compareTo.contains(tokens[i].getAddress())) {
4714a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    // Get the address here
4724a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    list.append(address + END_TOKEN);
4734a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                }
4744a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4764a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4774a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4784a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private HashSet<String> convertToHashSet(List<Rfc822Token[]> list) {
4794a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> hash = new HashSet<String>();
4804a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : list) {
4814a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                hash.add(tokens[i].getAddress());
4834a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4844a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4854a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return hash;
4864a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4874a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4884a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
4894a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        @VisibleForTesting
4904a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();
4914a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4924a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address: addresses) {
4934a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            tokenized.add(Rfc822Tokenizer.tokenize(address));
4944a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4954a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return tokenized;
4964a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4974a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4984a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
4994a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
5004a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address : addresses) {
5014a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addAddressToList(address, list);
5024a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5034a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5044a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5054a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addAddressToList(String address, RecipientEditTextView list) {
5064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (address == null || list == null)
5074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
5084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
5104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (int i = 0; i < tokens.length; i++) {
5124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            list.append(tokens[i] + END_TOKEN);
5134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
5174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected Collection<String> initToRecipients(String account, String accountEmail,
5184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            String senderAddress, String[] replyToAddresses, String[] inToAddresses) {
5194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // The To recipient is the reply-to address specified in the original
5204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, unless it is:
5214a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the current user OR a custom from of the current user, in which case
5224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // it's the To recipient list of the original message.
5234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // OR missing, in which case use the sender of the original message
5244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Set<String> toAddresses = Sets.newHashSet();
5254a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        Address sender = Address.getEmailAddress(senderAddress);
5264a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        if (sender != null && sender.getAddress().equalsIgnoreCase(account)) {
5274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // The sender address is this account, so reply acts like reply all.
5284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            toAddresses.addAll(Arrays.asList(inToAddresses));
5294a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else if (replyToAddresses != null && replyToAddresses.length != 0) {
5304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            toAddresses.addAll(Arrays.asList(replyToAddresses));
5314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else {
5324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Check to see if the sender address is one of the user's custom
5334a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // from addresses.
5344a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            if (senderAddress != null && sender != null
5354a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira                    && !accountEmail.equalsIgnoreCase(sender.getAddress())) {
5364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Replying to the sender of the original message is the most
5374a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // common case.
5384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                toAddresses.add(senderAddress);
5394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            } else {
5404a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // This happens if the user replies to a message they originally
5414a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // wrote. In this case, "reply" really means "re-send," so we
5424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // target the original recipients. This works as expected even
5434a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // if the user sent the original message to themselves.
5444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                toAddresses.addAll(Arrays.asList(inToAddresses));
5454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return toAddresses;
5484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5494a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static void addRecipients(String account, Set<String> recipients, String[] addresses) {
5514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String email : addresses) {
5524a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Do not add this account, or any of the custom froms, to the list
5534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // of recipients.
5544a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            final String recipientAddress = Address.getEmailAddress(email).getAddress();
5554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            if (!account.equalsIgnoreCase(recipientAddress)) {
5564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                recipients.add(email.replace("\"\"", ""));
5574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5584a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
55946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
56046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
56146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void setSubject(Cursor refMessage, int action) {
56246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String subject = refMessage.getString(UIProvider.MESSAGE_SUBJECT_COLUMN);
56346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String prefix;
56446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String correctedSubject = null;
56546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
56646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = "";
56746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else if (action == ComposeActivity.FORWARD) {
56846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.forward_subject_label);
56946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
57046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.reply_subject_label);
57146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
57246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
57346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        // Don't duplicate the prefix
57446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
57546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = subject;
57646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
57746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = String
57846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    .format(getString(R.string.formatted_subject), prefix, subject);
57946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
58046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mSubject.setText(correctedSubject);
58146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
58246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
583eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void setupRecipients() {
584eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mTo = setupRecipients(R.id.to);
585eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mCc = setupRecipients(R.id.cc);
586eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBcc = setupRecipients(R.id.bcc);
587eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
588eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
589c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView setupRecipients(int id) {
590c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        RecipientEditTextView view = (RecipientEditTextView) findViewById(id);
59133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        String accountName = mAccount.name;
59233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        view.setAdapter(new RecipientAdapter(this, accountName));
593c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        view.setTokenizer(new Rfc822Tokenizer());
59482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mValidator == null) {
59533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            int offset = accountName.indexOf("@") + 1;
59633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            String account = accountName;
597c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            if (offset > -1) {
59833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                account = account.substring(accountName.indexOf("@") + 1);
599c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            }
60082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValidator = new Rfc822Validator(account);
601c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        }
60282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        view.setValidator(mValidator);
603c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        return view;
6048e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
6058e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
6068e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
6078e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onClick(View v) {
6088e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        int id = v.getId();
6098e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        switch (id) {
610a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            case R.id.add_cc_bcc:
6118e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Verify that cc/ bcc aren't showing.
6128e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Animate in cc/bcc.
613a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                showCcBccViews();
6148e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                break;
6158e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
6168e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
617b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
618b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
619b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onCreateOptionsMenu(Menu menu) {
620b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        super.onCreateOptionsMenu(menu);
621b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        MenuInflater inflater = getMenuInflater();
622b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        inflater.inflate(R.menu.compose_menu, menu);
62382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSave = menu.findItem(R.id.save);
62482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSend = menu.findItem(R.id.send);
625b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return true;
626b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
627b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
628b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
629ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    public boolean onPrepareOptionsMenu(Menu menu) {
630ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        MenuItem ccBcc = menu.findItem(R.id.add_cc_bcc);
631ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (ccBcc != null) {
632ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            // Its possible there is a menu item OR a button.
633ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean ccFieldVisible = mCc.isShown();
634ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean bccFieldVisible = mBcc.isShown();
635ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!ccFieldVisible || !bccFieldVisible) {
636ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(true);
637ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setTitle(getString(!ccFieldVisible ? R.string.add_cc_label
638ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
639ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
640ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(false);
641ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
642ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
64375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        if (mSave != null) {
64475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mSave.setEnabled(shouldSave());
64575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
646ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        return true;
647ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
648ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
649ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    @Override
650b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onOptionsItemSelected(MenuItem item) {
651b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        int id = item.getItemId();
65275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        boolean handled = true;
653b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        switch (id) {
6547b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira            case R.id.add_attachment:
655013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                doAttach();
6567b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira                break;
657ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            case R.id.add_cc_bcc:
658ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                showCcBccViews();
659b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                break;
66033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.save:
661eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                doSave(true, false);
66233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
66333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.send:
66433fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                doSend();
66575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                break;
66675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            default:
66775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                handled = false;
66833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
669b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        }
670b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return !handled ? super.onOptionsItemSelected(item) : handled;
671b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
672326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
67333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private void doSend() {
67482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        sendOrSaveWithSanityChecks(false, true, false);
67533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
67633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
677eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void doSave(boolean showToast, boolean resetIME) {
678eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        sendOrSaveWithSanityChecks(true, showToast, false);
679eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
680eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
681eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
682eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
68382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
68482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
68582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ interface SendOrSaveCallback {
68682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);
68782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void notifyMessageIdAllocated(SendOrSaveMessage message, long messageId);
68882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public long getMessageId();
68982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);
69082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
69182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
69282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveTask implements Runnable {
69382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final Context mContext;
69482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final SendOrSaveCallback mSendOrSaveCallback;
69582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @VisibleForTesting
69682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final SendOrSaveMessage mSendOrSaveMessage;
69782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
69882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveTask(Context context, SendOrSaveMessage message,
69982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                SendOrSaveCallback callback) {
70082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mContext = context;
70182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback = callback;
70282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveMessage = message;
70382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
70482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
70582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @Override
70682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void run() {
70782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final SendOrSaveMessage message = mSendOrSaveMessage;
70882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
70982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final Account selectedAccount = message.mSelectedAccount;
71082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            long messageId = mSendOrSaveCallback.getMessageId();
71182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // If a previous draft has been saved, in an account that is different
71282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // than what the user wants to send from, remove the old draft, and treat this
71382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // as a new message
71482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!selectedAccount.equals(message.mAccount)) {
71582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
71682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentResolver resolver = mContext.getContentResolver();
71782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentValues values = new ContentValues();
71882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    values.put(BaseColumns._ID, messageId);
719eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    if (!TextUtils.isEmpty(selectedAccount.expungeMessageUri)) {
720eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                        resolver.update(Uri.parse(selectedAccount.expungeMessageUri), values, null,
721eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                                null);
722eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    }
72382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    // reset messageId to 0, so a new message will be created
72482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    messageId = UIProvider.INVALID_MESSAGE_ID;
72582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                }
72682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
72782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
72882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final long messageIdToSave = messageId;
72982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            int newDraftId = -1;
73082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (messageIdToSave != UIProvider.INVALID_MESSAGE_ID) {
73182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mContext.getContentResolver().update(
73282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        Uri.parse(message.mSave ? selectedAccount.saveDraftUri
73382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                : selectedAccount.sendMessageUri), message.mValues, null, null);
73482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            } else {
73582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                newDraftId = mContext.getContentResolver().update(
73682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        Uri.parse(message.mSave ? selectedAccount.saveDraftUri
73782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                : selectedAccount.sendMessageUri), message.mValues, null, null);
73882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
73982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // Broadcast notification that a new message id has been
74082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // allocated
74182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mSendOrSaveCallback.notifyMessageIdAllocated(message, newDraftId);
74282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
74382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
74482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!message.mSave) {
74582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
74682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.TO));
74782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
74882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.CC));
74982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
75082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.BCC));
75182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
75282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
75382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
75482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
75582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
75682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Array of the outstanding send or save tasks.  Access is synchronized
75782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // with the object itself
75882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /* package for testing */
75982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
76082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private int mRequestId;
76182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private long mDraftId;
76282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
76382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveMessage {
76482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mAccount;
76582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mSelectedAccount;
76682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final ContentValues mValues;
76782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final long mRefMessageId;
76882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final boolean mSave;
76982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final int mRequestId;
77082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
77182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveMessage(Account account, Account selectedAccount, ContentValues values,
77282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                long refMessageId, boolean save) {
77382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mAccount = account;
77482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSelectedAccount = selectedAccount;
77582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValues = values;
77682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRefMessageId = refMessageId;
77782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave = save;
77882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRequestId = mValues.hashCode() ^ hashCode();
77982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
78082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
78182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int requestId() {
78282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return mRequestId;
78382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
78482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
78582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
78682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
78782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the to recipients.
78882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
78982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getToAddresses() {
79082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mTo);
79182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
79482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the cc recipients.
79582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
79682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getCcAddresses() {
79782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mCc);
79882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
80082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
80182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the bcc recipients.
80282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
80382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getBccAddresses() {
80482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mBcc);
80582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
80682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
80782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getAddressesFromList(RecipientEditTextView list) {
80882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (list == null) {
80982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return new String[0];
81082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
81182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
81282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int count = tokens.length;
81382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        String[] result = new String[count];
81482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (int i = 0; i < count; i++) {
81582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            result[i] = tokens[i].toString();
81682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
81782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return result;
81882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
81982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
82082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
82182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check for invalid email addresses.
82282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param to String array of email addresses to check.
82382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param wrongEmailsOut Emails addresses that were invalid.
82482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
82582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void checkInvalidEmails(String[] to, List<String> wrongEmailsOut) {
82682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (String email : to) {
82782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!mValidator.isValid(email)) {
82882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                wrongEmailsOut.add(email);
82982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
83082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
83182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
83282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
83382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
83482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Show an error because the user has entered an invalid recipient.
83582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param message
83682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
83782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void showRecipientErrorDialog(String message) {
83882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Only 1 invalid recipients error dialog should be allowed up at a
83982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // time.
84082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mRecipientErrorDialog != null) {
84182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRecipientErrorDialog.dismiss();
84282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
84382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mRecipientErrorDialog = new AlertDialog.Builder(this).setMessage(message).setTitle(
84482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                R.string.recipient_error_dialog_title)
84582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
84682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setCancelable(false)
84782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setPositiveButton(
84882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        R.string.ok, new Dialog.OnClickListener() {
84982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            public void onClick(DialogInterface dialog, int which) {
85082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // after the user dismisses the recipient error
85182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // dialog we want to make sure to refocus the
85282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // recipient to field so they can fix the issue
85382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // easily
85482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                if (mTo != null) {
85582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                    mTo.requestFocus();
85682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                }
85782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                mRecipientErrorDialog = null;
85882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            }
85982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        }).show();
86082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
86182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
86282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
86382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Update the state of the UI based on whether or not the current draft
86482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * needs to be saved and the message is not empty.
86582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
866eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void updateSaveUi() {
86782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
86882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled((shouldSave() && !isBlank()));
86982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
87082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
87182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
87282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
87382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Returns true if we need to save the current draft.
87482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
87582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean shouldSave() {
87682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        synchronized (mDraftIdLock) {
87782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // The message should only be saved if:
87882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // It hasn't been sent AND
87982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Some text has been added to the message OR
88082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // an attachment has been added or removed
881eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return (mTextChanged || mAttachmentsChanged ||
88282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    (mReplyFromChanged && !isBlank()));
88382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
88482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
88582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
88682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
88782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check if the ComposeArea believes all fields are blank.
88882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @return boolean
88982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
89082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public boolean isBlank() {
89182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return mSubject.getText().length() == 0
892eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira               && mBodyView.getText().length() == 0
89382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mTo.length() == 0
89482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mCc.length() == 0
89582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mBcc.length() == 0
89682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mAttachmentsView.getAttachments().size() == 0;
89782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
89882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
89982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
90082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Allows any changes made by the user to be ignored. Called when the user
90182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * decides to discard a draft.
90282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
90382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void discardChanges() {
90482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = false;
905eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = false;
90682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mReplyFromChanged = false;
90733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
90833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
90982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
91082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
91182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param body
91282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param save
91382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param showToast
91482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return Whether the send or save succeeded.
91582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
91682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean sendOrSaveWithSanityChecks(final boolean save,
91782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               final boolean showToast, final boolean orientationChanged) {
91882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String[] to, cc, bcc;
919eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       Editable body = mBodyView.getEditableText();
92082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
92182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (orientationChanged) {
92282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = cc = bcc = new String[0];
92382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       } else {
92482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = getToAddresses();
92582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           cc = getCcAddresses();
92682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           bcc = getBccAddresses();
92782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
92882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
92982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't let the user send to nobody (but it's okay to save a message with no recipients)
93082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
93182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           showRecipientErrorDialog(getString(R.string.recipient_needed));
93282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return false;
93382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
93482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
93582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       List<String> wrongEmails = new ArrayList<String>();
93682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
93782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(to, wrongEmails);
93882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(cc, wrongEmails);
93982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(bcc, wrongEmails);
94082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
94182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
94282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't let the user send an email with invalid recipients
94382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (wrongEmails.size() > 0) {
94482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           String errorText =
94582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               String.format(getString(R.string.invalid_recipient), wrongEmails.get(0));
94682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           showRecipientErrorDialog(errorText);
94782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return false;
94882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
94982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
95082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
95182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           public void onClick(DialogInterface dialog, int which) {
952eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira               sendOrSave(mBodyView.getEditableText(), save, showToast, orientationChanged);
95382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
95482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       };
95582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
95682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Show a warning before sending only if there are no attachments.
95782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
95882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
95982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean warnAboutEmptySubject = isSubjectEmpty();
96082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean emptyBody = TextUtils.getTrimmedLength(body) == 0;
96182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
96282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // A warning about an empty body may not be warranted when
96382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // forwarding mails, since a common use case is to forward
96482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // quoted text and not append any more text.
96582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
96682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
96782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // When we bring up a dialog warning the user about a send,
96882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // assume that they accept sending the message. If they do not, the dialog
96982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // listener is required to enable sending again.
97082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               if (warnAboutEmptySubject) {
97182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, listener);
97282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   return true;
97382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
97482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
97582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               if (warnAboutEmptyBody) {
97682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   showSendConfirmDialog(R.string.confirm_send_message_with_no_body, listener);
97782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   return true;
97882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
97982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
98082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // Ask for confirmation to send (if always required)
98182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (showSendConfirmation()) {
98282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               showSendConfirmDialog(R.string.confirm_send_message, listener);
98382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               return true;
98482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
98582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
98682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
98782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       sendOrSave(body, save, showToast, false);
98882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return true;
98982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
99082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
99182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
99282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns a boolean indicating whether warnings should be shown for empty
99382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * subject and body fields
99482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
99582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return True if a warning should be shown for empty text fields
99682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
99782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean showEmptyTextWarnings() {
99882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return mAttachmentsView.getAttachments().size() == 0;
99982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
100082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
100182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
100282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns a boolean indicating whether the user should confirm each send
100382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
100482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return True if a warning should be on each send
100582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
100682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean showSendConfirmation() {
100782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // TODO: read user preference for whether or not to show confirm send dialog.
100882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return true;
100982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
101082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
101182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void showSendConfirmDialog(int messageId, DialogInterface.OnClickListener listener) {
101282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mSendConfirmDialog != null) {
101382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendConfirmDialog.dismiss();
101482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendConfirmDialog = null;
101582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
101682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mSendConfirmDialog = new AlertDialog.Builder(this)
101782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setMessage(messageId)
101882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setTitle(R.string.confirm_send_title)
101982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setIconAttribute(android.R.attr.alertDialogIcon)
102082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setPositiveButton(R.string.send, listener)
102182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setNegativeButton(R.string.cancel, this)
102282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setCancelable(false)
102382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .show();
102482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
102582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
102682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
102782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns whether the ComposeArea believes there is any text in the body of
102882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * the composition. TODO: When ComposeArea controls the Body as well, add
102982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * that here.
103082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
103182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   public boolean isBodyEmpty() {
103282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return !mQuotedTextView.isTextIncluded();
103382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
103482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
103582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
103682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Test to see if the subject is empty.
103782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return boolean.
103882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
103982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   // TODO: this will likely go away when composeArea.focus() is implemented
104082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   // after all the widget control is moved over.
104182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   public boolean isSubjectEmpty() {
104282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
104382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
104482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
104582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /* package */
104682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   static int sendOrSaveInternal(Context context, final Account account,
104782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final Account selectedAccount, String fromAddress, final Spanned body, final String[] to,
104882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final String[] cc, final String[] bcc, final String subject,
104982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final CharSequence quotedText, final List<Attachment> attachments,
105082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final long refMessageId, SendOrSaveCallback callback, Handler handler, boolean save,
105182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           boolean forward) {
105282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       ContentValues values = new ContentValues();
105382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
105482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putToAddresses(values, to);
105582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putCcAddresses(values, cc);
105682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putBccAddresses(values, bcc);
105782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
105882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putSubject(values, subject);
105982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String htmlBody = Html.toHtml(body);
106082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
106182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       StringBuilder fullBody = new StringBuilder(htmlBody);
106282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (includeQuotedText) {
106382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (forward) {
106482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // forwarded messages get full text in HTML from client
106582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               fullBody.append(quotedText);
106682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               MessageModification.putForward(values, forward);
106782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           } else {
106882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // replies get full quoted text from server - HTMl gets converted to text for now
106982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               final String text = quotedText.toString();
1070913897e6879f5f2713b56f13022bd784cf58a585Mindy Pereira               if (QuotedTextView.containsQuotedText(text)) {
1071913897e6879f5f2713b56f13022bd784cf58a585Mindy Pereira                   int pos = QuotedTextView.getQuotedTextOffset(text);
107282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   fullBody.append(text.substring(0, pos));
107382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   int quoteStartPos = fullBody.length();
107482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putForward(values, forward);
107582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putIncludeQuotedText(values, includeQuotedText);
107682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putQuoteStartPos(values, quoteStartPos);
107782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               } else {
107882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   LogUtils.w(LOG_TAG, "Couldn't find quoted text");
107982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   // This shouldn't happen, but just use what we have,
108082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   //  and don't do server-side expansion
108182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   fullBody.append(text);
108282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
108382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
108482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
108582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putBody(values, fullBody.toString());
108682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
108782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(account, selectedAccount,
108882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               values, refMessageId, save);
108982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
109082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       callback.initializeSendOrSave(sendOrSaveTask);
109282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Do the send/save action on the specified handler to avoid possible ANRs
109482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       handler.post(sendOrSaveTask);
109582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return sendOrSaveMessage.requestId();
109782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
109882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void sendOrSave(Spanned body, boolean save, boolean showToast,
110082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           boolean orientationChanged) {
110182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Check if user is a monkey. Monkeys can compose and hit send
110282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // button but are not allowed to send anything off the device.
110382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save && ActivityManager.isUserAMonkey()) {
110482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return;
110582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
110682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
110782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String[] to, cc, bcc;
110882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (orientationChanged) {
110982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = cc = bcc = new String[0];
111082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       } else {
111182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = getToAddresses();
111282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           cc = getCcAddresses();
111382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           bcc = getBccAddresses();
111482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
111582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
111682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
111782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveCallback callback = new SendOrSaveCallback() {
111882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               private long mDraftId;
111982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            private int mRestoredRequestId;
112082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
112182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
112282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mActiveTasks) {
112382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       int numTasks = mActiveTasks.size();
112482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       if (numTasks == 0) {
112582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // Start service so we won't be killed if this app is put in the
112682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // background.
112782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           startService(new Intent(ComposeActivity.this, EmptyService.class));
112882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       }
112982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
113082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mActiveTasks.add(sendOrSaveTask);
113182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
113282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
113382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
113482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
113582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
113682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
113782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public void notifyMessageIdAllocated(SendOrSaveMessage message, long messageId) {
113882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mDraftIdLock) {
113982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mDraftId = messageId;
114082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sRequestMessageIdMap.put(message.requestId(), messageId);
114182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
114282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Cache request message map, in case the process is killed
114382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       saveRequestMap();
114482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
114582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
114682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.notifyMessageIdAllocated(message, messageId);
114782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
114882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
114982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
115082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public long getMessageId() {
115182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mDraftIdLock) {
115282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       if (mDraftId == UIProvider.INVALID_MESSAGE_ID) {
115382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // We don't have the message Id, check to see if we have a restored
115482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // request id, and see if we have a message for that request.
115582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           if (mRestoredRequestId != 0) {
115682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               Long retrievedMessageId =
115782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                       sRequestMessageIdMap.get(mRestoredRequestId);
115882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               if (retrievedMessageId != null) {
115982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                   mDraftId = retrievedMessageId.longValue();
116082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               }
116182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           }
116282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       }
116382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       return mDraftId;
116482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
116582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
116682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
116782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
116882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (success) {
116982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Successfully sent or saved so reset change markers
117082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       discardChanges();
117182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   } else {
117282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // A failure happened with saving/sending the draft
117382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // TODO(pwestbro): add a better string that should be used when failing to
117482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // send or save
117582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       Toast.makeText(ComposeActivity.this, R.string.send_failed,
117682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               Toast.LENGTH_SHORT).show();
117782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
117882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
117982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   int numTasks;
118082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mActiveTasks) {
118182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Remove the task from the list of active tasks
118282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mActiveTasks.remove(task);
118382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       numTasks = mActiveTasks.size();
118482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
118582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
118682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (numTasks == 0) {
118782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Stop service so we can be killed.
118882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       stopService(new Intent(ComposeActivity.this, EmptyService.class));
118982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
119082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
119182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
119282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
119382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
119482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira         };
119582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
119682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Get the selected account if the from spinner has been setup.
1197eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       Account selectedAccount = mAccount;
1198eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       String fromAddress = selectedAccount.name;
119982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (selectedAccount == null || fromAddress == null) {
120082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // We don't have either the selected account or from address,
120182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // use mAccount.
1202eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira           selectedAccount = mAccount;
1203eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira           fromAddress = mAccount.name;
120482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
120582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
120682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mSendSaveTaskHandler == null) {
120782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
120882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           handlerThread.start();
120982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
121082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
121182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
121282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
121382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mRequestId = sendOrSaveInternal(this, mAccount, selectedAccount, fromAddress, body,
121482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               to, cc, bcc, mSubject.getText().toString(), mQuotedTextView.getQuotedText(),
121582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               mAttachmentsView.getAttachments(), mRefMessageId, callback, mSendSaveTaskHandler,
121682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               save, mForward);
121782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
121882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mRecipient != null && mRecipient.equals(mAccount.name)) {
121982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mRecipient = selectedAccount.name;
122082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
122182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mAccount = selectedAccount;
122282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
122382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't display the toast if the user is just changing the orientation, but we still
122482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // need to save the draft to the cursor because this is how we restore the attachments
122582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // when the configuration change completes.
122682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
122782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
122882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   Toast.LENGTH_LONG).show();
122982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
123082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
123182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Need to update variables here
123282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // because the send or save completes asynchronously even though the
123382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // toast shows right away.
123482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       discardChanges();
1235eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       updateSaveUi();
123682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
123782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // If we are sending, finish the activity
123882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
123982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           finish();
124082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
124182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
124282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
124382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
124482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Save the state of the request messageid map.  This allows for the Gmail process
124582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * to be killed, but and still allow for ComposeActivity instances to be recreated
124682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * correctly.
124782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
124882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void saveRequestMap() {
124982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // TODO: store the request map in user preferences.
125082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
125182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1252013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void doAttach() {
1253013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
1254013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        i.addCategory(Intent.CATEGORY_OPENABLE);
1255013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        if (Settings.System.getInt(
1256013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                getContentResolver(), UIProvider.getAttachmentTypeSetting(), 0) != 0) {
1257013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("*/*");
1258013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        } else {
1259013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("image/*");
1260013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
1261013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        mAddingAttachment = true;
1262013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        startActivityForResult(Intent.createChooser(i,
1263013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                getText(R.string.select_attachment_type)), RESULT_PICK_ATTACHMENT);
1264013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
1265013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
1266ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    private void showCcBccViews() {
1267a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccView.show(true, true, true);
1268ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
1269ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            mCcBccButton.setVisibility(View.GONE);
1270ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
1271ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
1272ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
1273326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    @Override
1274326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public boolean onNavigationItemSelected(int position, long itemId) {
1275a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        int initialComposeMode = mComposeMode;
1276326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (position == ComposeActivity.REPLY) {
1277326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY;
1278326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.REPLY_ALL) {
1279326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY_ALL;
1280326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.FORWARD) {
1281326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.FORWARD;
1282326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1283a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (initialComposeMode != mComposeMode) {
1284154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            resetMessageForModeChange();
128533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(mComposeMode, mAccount.name);
1286a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
1287326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        return true;
1288326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
1289326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1290154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    private void resetMessageForModeChange() {
1291154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // When switching between reply, reply all, forward,
1292154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // follow the behavior of webview.
1293154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // The contents of the following fields are cleared
1294154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // so that they can be populated directly from the
1295154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // ref message:
1296154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Any recipient fields
1297154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) The subject
1298154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mTo.setText("");
1299154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mCc.setText("");
1300154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mBcc.setText("");
1301154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any edits to the subject are replaced with the original subject.
1302154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mSubject.setText("");
1303154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1304154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any changes to the contents of the following fields are kept:
1305154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Body
1306154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) Attachments
1307154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // If the user made changes to attachments, keep their changes.
1308154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        if (!mAttachmentsChanged) {
1309154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            mAttachmentsView.deleteAllAttachments();
1310154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        }
1311154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    }
1312154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1313326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private class ComposeModeAdapter extends ArrayAdapter<String> {
1314326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1315326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater mInflater;
1316326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1317326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public ComposeModeAdapter(Context context) {
1318326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            super(context, R.layout.compose_mode_item, R.id.mode, getResources()
1319326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    .getStringArray(R.array.compose_modes));
1320326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1321326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1322326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater getInflater() {
1323326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mInflater == null) {
1324326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mInflater = LayoutInflater.from(getContext());
1325326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1326326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return mInflater;
1327326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1328326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1329326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        @Override
1330326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public View getView(int position, View convertView, ViewGroup parent) {
1331326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (convertView == null) {
1332326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
1333326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1334326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
1335326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return super.getView(position, convertView, parent);
1336326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1337326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
133846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
133946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    @Override
134046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void onRespondInline(String text) {
134146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        appendToBody(text, false);
134246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
134346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
134446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
134546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Append text to the body of the message. If there is no existing body
134646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * text, just sets the body to text.
134746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
134846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
134946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
135046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
135146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void appendToBody(CharSequence text, boolean withSignature) {
1352eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        Editable bodyText = mBodyView.getEditableText();
135346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (bodyText != null && bodyText.length() > 0) {
135446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            bodyText.append(text);
135546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
135646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            setBody(text, withSignature);
135746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
135846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
135946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
136046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
136146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Set the body of the message.
136246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
136346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
136446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
136546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void setBody(CharSequence text, boolean withSignature) {
1366eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.setText(text);
136746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
13681a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
13695a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    @Override
13705a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    public void onAccountChanged() {
13715a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        Account selectedAccountInfo = mFromSpinner.getCurrentAccount();
1372eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAccount = selectedAccountInfo;
137382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1374eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // TODO: handle discarding attachments when switching accounts.
13755a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        // Only enable save for this draft if there is any other content
13765a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        // in the message.
13775a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        if (!isBlank()) {
13785a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira            enableSave(true);
137982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
13805a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mReplyFromChanged = true;
13815a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        setupRecipients();
13821a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
138382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
138482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSave(boolean enabled) {
138582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
138682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled(enabled);
138782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
138882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
138982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
139082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSend(boolean enabled) {
139182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSend != null) {
139282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSend.setEnabled(enabled);
139382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
139482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
139582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
139682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
139782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Handles button clicks from any error dialogs dealing with sending
139882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * a message.
139982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
140082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
140182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onClick(DialogInterface dialog, int which) {
140282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        switch (which) {
140382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_POSITIVE: {
140482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                doDiscardWithoutConfirmation(true /* show toast */ );
140582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
140682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
140782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_NEGATIVE: {
140882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // If the user cancels the send, re-enable the send button.
140982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                enableSend(true);
141082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
141182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
141282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
141382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
141582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
141782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Effectively discard the current message.
141882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     *
141982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This method is either invoked from the menu or from the dialog
142082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * once the user has confirmed that they want to discard the message.
142182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param showToast show "Message discarded" toast if true
142282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
142382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void doDiscardWithoutConfirmation(boolean showToast) {
142482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        synchronized (mDraftIdLock) {
142582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
142682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                ContentValues values = new ContentValues();
142782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                values.put(MessageColumns.SERVER_ID, mDraftId);
1428eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                getContentResolver().update(Uri.parse(mAccount.expungeMessageUri),
142982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        values, null, null);
143082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // This is not strictly necessary (since we should not try to
143182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // save the draft after calling this) but it ensures that if we
143282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // do save again for some reason we make a new draft rather than
143382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // trying to resave an expunged draft.
143482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mDraftId = UIProvider.INVALID_MESSAGE_ID;
143582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
143682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
143782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
143882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (showToast) {
143982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Display a toast to let the user know
144082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();
144182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
144282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
144382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // This prevents the draft from being saved in onPause().
144482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        discardChanges();
144582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        finish();
144682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
144782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1448eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeeded() {
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            doSave(!mAddingAttachment /* show toast */, true /* reset IME */);
1458eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1459eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1460eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1461eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeededOnOrientationChanged() {
1462eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1463eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1464eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1465eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1466eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1467eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1468eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1469eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1470eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSaveOrientationChanged(!mAddingAttachment /* show toast */, true /* reset IME */);
1471eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1472eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1473eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1474eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    /**
1475eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     * Save a draft if a draft already exists or the message is not empty.
1476eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     */
1477eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void doSaveOrientationChanged(boolean showToast, boolean resetIME) {
1478eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveOnOrientationChanged();
1479eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
1480eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
1481eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
1482eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1483eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1484eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1485eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected boolean saveOnOrientationChanged() {
1486eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        return sendOrSaveWithSanityChecks(true, false, true);
1487eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1488eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1489eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
1490eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void onAttachmentDeleted() {
1491eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = true;
1492eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
1493eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
149475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
149575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
149675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    /**
149775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     * This is called any time one of our text fields changes.
149875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     */
149975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void afterTextChanged(Editable s) {
150075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTextChanged = true;
150175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        updateSaveUi();
150275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
150375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
150475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    @Override
150575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
150675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
150775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
150875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
150975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void onTextChanged(CharSequence s, int start, int before, int count) {
151075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
151175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
151275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
151375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
151475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // There is a big difference between the text associated with an address changing
151575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // to add the display name or to format properly and a recipient being added or deleted.
151675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Make sure we only notify of changes when a recipient has been added or deleted.
151775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private class RecipientTextWatcher implements TextWatcher {
151875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private HashMap<String, Integer> mContent = new HashMap<String, Integer>();
151975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private RecipientEditTextView mView;
152175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private TextWatcher mListener;
152375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public RecipientTextWatcher(RecipientEditTextView view, TextWatcher listener) {
152575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mView = view;
152675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mListener = listener;
152775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
152875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
153075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void afterTextChanged(Editable s) {
153175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (hasChanged()) {
153275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                mListener.afterTextChanged(s);
153375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
153475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
153575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
153675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private boolean hasChanged() {
153775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] currRecips = tokenizeRecips(getAddressesFromList(mView));
153875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalCount = currRecips.length;
153975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalPrevCount = 0;
154075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (Entry<String, Integer> entry : mContent.entrySet()) {
154175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                totalPrevCount += entry.getValue();
154275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
154375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (totalCount != totalPrevCount) {
154475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                return true;
154575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
154675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
154775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : currRecips) {
154875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
154975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    return true;
155075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
155175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    int count = mContent.get(recip) - 1;
155275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    if (count < 0) {
155375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        return true;
155475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    } else {
155575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        mContent.put(recip, count);
155675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    }
155775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
155875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
155975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return false;
156075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
156175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
156275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private String[] tokenizeRecips(String[] recips) {
156375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Tokenize them all and put them in the list.
156475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recipAddresses = new String[recips.length];
156575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (int i = 0; i < recips.length; i++) {
156675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                recipAddresses[i] = Rfc822Tokenizer.tokenize(recips[i])[0].getAddress();
156775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
156875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return recipAddresses;
156975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
157075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
157175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
157275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
157375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recips = tokenizeRecips(getAddressesFromList(mView));
157475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : recips) {
157575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
157675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, 1);
157775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
157875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, (mContent.get(recip)) + 1);
157975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
158075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
158175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
158275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
158375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
158475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void onTextChanged(CharSequence s, int start, int before, int count) {
158575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Do nothing.
158675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
158775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
15888e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira}