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