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