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