blob: d9be835d6ae22502b8a16226802c11d087946a4d [file] [log] [blame]
Mindy Pereira8e9305e2011-12-13 14:25:04 -08001/**
2 * Copyright (c) 2011, Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Minh Pham47d8d442011-12-13 17:07:13 -080017package com.android.email.compose;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080018
Mindy Pereirac17d0732011-12-29 10:46:19 -080019import android.accounts.Account;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080020import android.animation.Animator;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
Mindy Pereira326c6602012-01-04 15:32:42 -080023import android.app.ActionBar;
24import android.app.ActionBar.OnNavigationListener;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080025import android.app.Activity;
Mindy Pereira6349a042012-01-04 11:25:01 -080026import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.database.Cursor;
30import android.net.Uri;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080031import android.os.Bundle;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080032import android.text.Editable;
33import android.text.TextUtils;
34import android.text.util.Rfc822Token;
Mindy Pereirac17d0732011-12-29 10:46:19 -080035import android.text.util.Rfc822Tokenizer;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080036import android.util.TimingLogger;
Mindy Pereira326c6602012-01-04 15:32:42 -080037import android.view.LayoutInflater;
Mindy Pereirab47f3e22011-12-13 14:25:04 -080038import android.view.Menu;
39import android.view.MenuInflater;
40import android.view.MenuItem;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080041import android.view.View;
Mindy Pereira326c6602012-01-04 15:32:42 -080042import android.view.ViewGroup;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080043import android.view.View.OnClickListener;
Mindy Pereira1a95a572012-01-05 12:21:29 -080044import android.widget.AdapterView;
45import android.widget.AdapterView.OnItemSelectedListener;
Mindy Pereira326c6602012-01-04 15:32:42 -080046import android.widget.ArrayAdapter;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080047import android.widget.Button;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080048import android.widget.LinearLayout;
Mindy Pereira1a95a572012-01-05 12:21:29 -080049import android.widget.Spinner;
Mindy Pereira6349a042012-01-04 11:25:01 -080050import android.widget.TextView;
Mindy Pereira7b56a612011-12-14 12:32:28 -080051
Mindy Pereirac17d0732011-12-29 10:46:19 -080052import com.android.common.Rfc822Validator;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080053import com.android.email.compose.QuotedTextView.RespondInlineListener;
Mindy Pereira6349a042012-01-04 11:25:01 -080054import com.android.email.providers.UIProvider;
Mindy Pereira326c6602012-01-04 15:32:42 -080055import com.android.email.providers.Attachment;
Mindy Pereira7b56a612011-12-14 12:32:28 -080056import com.android.email.providers.protos.mock.MockAttachment;
Minh Pham47d8d442011-12-13 17:07:13 -080057import com.android.email.R;
Mindy Pereira1a95a572012-01-05 12:21:29 -080058import com.android.email.utils.AccountUtils;
Mindy Pereira7b56a612011-12-14 12:32:28 -080059import com.android.email.utils.MimeType;
Mindy Pereira6349a042012-01-04 11:25:01 -080060import com.android.email.utils.Utils;
Mindy Pereirac17d0732011-12-29 10:46:19 -080061import com.android.ex.chips.RecipientEditTextView;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080062import com.google.common.annotations.VisibleForTesting;
Mindy Pereira8e9305e2011-12-13 14:25:04 -080063
Mindy Pereira46ce0b12012-01-05 10:32:15 -080064import java.text.DateFormat;
65import java.util.ArrayList;
66import java.util.Arrays;
67import java.util.Collection;
Mindy Pereira1a95a572012-01-05 12:21:29 -080068import java.util.Collections;
Mindy Pereira46ce0b12012-01-05 10:32:15 -080069import java.util.Date;
70import java.util.HashSet;
71import java.util.List;
72
73public class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
Mindy Pereira1a95a572012-01-05 12:21:29 -080074 RespondInlineListener, OnItemSelectedListener {
Mindy Pereira6349a042012-01-04 11:25:01 -080075 // Identifiers for which type of composition this is
76 static final int COMPOSE = -1; // also used for editing a draft
77 static final int REPLY = 0;
78 static final int REPLY_ALL = 1;
79 static final int FORWARD = 2;
80
Mindy Pereira46ce0b12012-01-05 10:32:15 -080081 // HTML tags used to quote reply content
82 // The following style must be in-sync with
83 // pinto.app.MessageUtil.QUOTE_STYLE and
84 // java/com/google/caribou/ui/pinto/modules/app/messageutil.js
85 // BEG_QUOTE_BIDI is also available there when we support BIDI
86 private static final String BLOCKQUOTE_BEGIN = "<blockquote class=\"quote\" style=\""
87 + "margin:0 0 0 .8ex;" + "border-left:1px #ccc solid;" + "padding-left:1ex\">";
88 private static final String BLOCKQUOTE_END = "</blockquote>";
89 // HTML tags used to quote replies & forwards
90 /* package for testing */static final String QUOTE_BEGIN = "<div class=\"quote\">";
91 private static final String QUOTE_END = "</div>";
92 // Separates the attribution headers (Subject, To, etc) from the body in
93 // quoted text.
94 /* package for testing */ static final String HEADER_SEPARATOR = "<br type='attribution'>";
95 private static final int HEADER_SEPARATOR_LENGTH = HEADER_SEPARATOR.length();
96
Mindy Pereira6349a042012-01-04 11:25:01 -080097 // Integer extra holding one of the above compose action
98 private static final String EXTRA_ACTION = "action";
99
100 /**
101 * Notifies the {@code Activity} that the caller is an Email
102 * {@code Activity}, so that the back behavior may be modified accordingly.
103 *
104 * @see #onAppUpPressed
105 */
106 private static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
107
108 // If this is a reply/forward then this extra will hold the original message uri
109 private static final String EXTRA_IN_REFERENCE_TO_MESSAGE_URI = "in-reference-to-uri";
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800110
Mindy Pereirac17d0732011-12-29 10:46:19 -0800111 private RecipientEditTextView mTo;
112 private RecipientEditTextView mCc;
113 private RecipientEditTextView mBcc;
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800114 private Button mCcBccButton;
115 private CcBccView mCcBccView;
Mindy Pereira7b56a612011-12-14 12:32:28 -0800116 private AttachmentsView mAttachmentsView;
Mindy Pereirac17d0732011-12-29 10:46:19 -0800117 private String mAccount;
118 private Rfc822Validator mRecipientValidator;
Mindy Pereira6349a042012-01-04 11:25:01 -0800119 private Uri mRefMessageUri;
120 private TextView mSubject;
121
Mindy Pereira326c6602012-01-04 15:32:42 -0800122 private ActionBar mActionBar;
123 private ComposeModeAdapter mComposeModeAdapter;
124 private int mComposeMode = -1;
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800125 private boolean mForward;
126 private String mRecipient;
127 private boolean mAttachmentsChanged;
128 private QuotedTextView mQuotedTextView;
129 private TextView mBodyText;
Mindy Pereira1a95a572012-01-05 12:21:29 -0800130 private View mFromStatic;
131 private View mFromSpinner;
132 private Spinner mFrom;
133 private List<String[]> mReplyFromAccounts;
134 private boolean mAccountSpinnerReady;
135 private String[] mCurrentReplyFromAccount;
136 private boolean mMessageIsForwardOrReply;
137 private List<String> mAccounts;
Mindy Pereira326c6602012-01-04 15:32:42 -0800138
139 /**
140 * Can be called from a non-UI thread.
141 */
142 public static void editDraft(Context context, String account, long mLocalMessageId) {
143 }
144
Mindy Pereira6349a042012-01-04 11:25:01 -0800145 /**
146 * Can be called from a non-UI thread.
147 */
148 public static void compose(Context launcher, String account) {
149 launch(launcher, account, null, COMPOSE);
150 }
151
152 /**
153 * Can be called from a non-UI thread.
154 */
155 public static void reply(Context launcher, String account, String uri) {
156 launch(launcher, account, uri, REPLY);
157 }
158
159 /**
160 * Can be called from a non-UI thread.
161 */
162 public static void replyAll(Context launcher, String account, String uri) {
163 launch(launcher, account, uri, REPLY_ALL);
164 }
165
166 /**
167 * Can be called from a non-UI thread.
168 */
169 public static void forward(Context launcher, String account, String uri) {
170 launch(launcher, account, uri, FORWARD);
171 }
172
173 private static void launch(Context launcher, String account, String uri, int action) {
174 Intent intent = new Intent(launcher, ComposeActivity.class);
175 intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
176 intent.putExtra(EXTRA_ACTION, action);
177 intent.putExtra(Utils.EXTRA_ACCOUNT, account);
178 intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI, uri);
179 launcher.startActivity(intent);
180 }
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800181
182 @Override
183 public void onCreate(Bundle savedInstanceState) {
184 super.onCreate(savedInstanceState);
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800185 Intent intent = getIntent();
Mindy Pereira3528d362012-01-05 14:39:44 -0800186 mAccount = intent.getStringExtra(Utils.EXTRA_ACCOUNT);
187 setContentView(R.layout.compose);
188 findViews();
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800189 int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800190 if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
191 mRefMessageUri = Uri.parse(intent.getStringExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI));
192 initFromRefMessage(action, mAccount);
193 } else {
194 setQuotedTextVisibility(false);
195 }
Mindy Pereira1a95a572012-01-05 12:21:29 -0800196 initActionBar(action);
197 asyncInitFromSpinner();
198 }
199
200 @Override
201 protected void onResume() {
202 super.onResume();
203 // Update the from spinner as other accounts
204 // may now be available.
205 asyncInitFromSpinner();
206 }
207
208 private void asyncInitFromSpinner() {
209 Account[] result = AccountUtils.getSyncingAccounts(this, null, null, null);
210 mAccounts = AccountUtils
211 .mergeAccountLists(mAccounts, result, true /* prioritizeAccountList */);
212 createReplyFromCache();
213 initFromSpinner();
214 }
215
216 /**
217 * Create a cache of all accounts a user could send mail from
218 */
219 private void createReplyFromCache() {
220 // Check for replyFroms.
221 List<String> accounts = null;
222 mReplyFromAccounts = new ArrayList<String[]>();
223
224 if (mMessageIsForwardOrReply) {
225 accounts = Collections.singletonList(mAccount);
226 } else {
227 accounts = mAccounts;
228 }
229 for (String account : accounts) {
230 // First add the account. First position is account, second
231 // is display of account, 3rd position is the REAL account this
232 // is being sent from / synced to.
233 mReplyFromAccounts.add(new String[] {
234 account, account, account, "false"
235 });
236 }
237 }
238
239 private void initFromSpinner() {
240 // If there are not yet any accounts in the cached synced accounts
241 // because this is the first time Gmail was opened, and it was opened directly
242 // to the compose activity,don't bother populating the reply from spinner yet.
243 if (mReplyFromAccounts == null || mReplyFromAccounts.size() == 0) {
244 mAccountSpinnerReady = false;
245 return;
246 }
247 FromAddressSpinnerAdapter adapter = new FromAddressSpinnerAdapter(this);
248 int currentAccountIndex = 0;
249 String replyFromAccount = mAccount;
250
251 boolean checkRealAccount = mRecipient == null || mAccount.equals(mRecipient);
252
253 currentAccountIndex = addAccountsToAdapter(adapter, checkRealAccount, replyFromAccount);
254
255 mFrom.setAdapter(adapter);
256 mFrom.setSelection(currentAccountIndex, false);
257 mFrom.setOnItemSelectedListener(this);
258 mCurrentReplyFromAccount = mReplyFromAccounts.get(currentAccountIndex);
259
260 hideOrShowFromSpinner();
261 mAccountSpinnerReady = true;
262 adapter.setSpinner(mFrom);
263 }
264
265 private void hideOrShowFromSpinner() {
266 // Determine whether the from account spinner or the static
267 // from text should be show
268 // When the spinner is shown, the static from text
269 // is hidden
270 showFromSpinner(mFrom.getCount() > 1);
271 }
272
273 private int addAccountsToAdapter(FromAddressSpinnerAdapter adapter, boolean checkRealAccount,
274 String replyFromAccount) {
275 int currentIndex = 0;
276 int currentAccountIndex = 0;
277 // Get the position of the current account
278 for (String[] account : mReplyFromAccounts) {
279 // Add the account to the Adapter
280 // The reason that we are not adding the Account array, but adding
281 // the names of each account, is because Account returns a string
282 // that we don't want to display on toString()
283 adapter.add(account);
284 // Compare to the account address, not the real account being
285 // sent from.
286 if (checkRealAccount) {
287 // Need to check the real account and the account address
288 // so that we can send from the correct address on the
289 // correct account when the same address may exist across
290 // multiple accounts.
291 if (account[FromAddressSpinnerAdapter.REAL_ACCOUNT].equals(mAccount)
292 && account[FromAddressSpinnerAdapter.ACCOUNT_ADDRESS]
293 .equals(replyFromAccount)) {
294 currentAccountIndex = currentIndex;
295 }
296 } else {
297 // Just need to check the account address.
298 if (replyFromAccount.equals(
299 account[FromAddressSpinnerAdapter.ACCOUNT_ADDRESS])) {
300 currentAccountIndex = currentIndex;
301 }
302 }
303
304 currentIndex++;
305 }
306 return currentAccountIndex;
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800307 }
308
309 private void findViews() {
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800310 mCcBccButton = (Button) findViewById(R.id.add_cc);
311 if (mCcBccButton != null) {
312 mCcBccButton.setOnClickListener(this);
313 }
314 mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
Mindy Pereira7b56a612011-12-14 12:32:28 -0800315 mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
Mindy Pereirac17d0732011-12-29 10:46:19 -0800316 mTo = setupRecipients(R.id.to);
317 mCc = setupRecipients(R.id.cc);
318 mBcc = setupRecipients(R.id.bcc);
Mindy Pereira6349a042012-01-04 11:25:01 -0800319 mSubject = (TextView) findViewById(R.id.subject);
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800320 mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
321 mQuotedTextView.setRespondInlineListener(this);
322 mBodyText = (TextView) findViewById(R.id.body);
Mindy Pereira1a95a572012-01-05 12:21:29 -0800323 mFromStatic = findViewById(R.id.static_from_content);
324 mFromSpinner = findViewById(R.id.spinner_from_content);
325 mFrom = (Spinner) findViewById(R.id.from_picker);
326 }
327
328 /**
329 * Show the static from text view or the spinner
330 * @param showSpinner Whether the spinner should be shown
331 */
332 private void showFromSpinner(boolean showSpinner) {
333 // show/hide the static text
334 mFromStatic.setVisibility(
335 showSpinner ? View.GONE : View.VISIBLE);
336
337 // show/hide the spinner
338 mFromSpinner.setVisibility(
339 showSpinner ? View.VISIBLE : View.GONE);
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800340 }
341
342 private void setQuotedTextVisibility(boolean show) {
343 mQuotedTextView.setVisibility(show ? View.VISIBLE : View.GONE);
Mindy Pereira6349a042012-01-04 11:25:01 -0800344 }
345
Mindy Pereira326c6602012-01-04 15:32:42 -0800346 private void initActionBar(int action) {
347 mComposeMode = action;
348 mActionBar = getActionBar();
349 if (action == ComposeActivity.COMPOSE) {
350 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
351 mActionBar.setTitle(R.string.compose);
352 } else {
353 mActionBar.setTitle(null);
354 if (mComposeModeAdapter == null) {
355 mComposeModeAdapter = new ComposeModeAdapter(this);
356 }
357 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
358 mActionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
359 switch (action) {
360 case ComposeActivity.REPLY:
361 mActionBar.setSelectedNavigationItem(0);
362 break;
363 case ComposeActivity.REPLY_ALL:
364 mActionBar.setSelectedNavigationItem(1);
365 break;
366 case ComposeActivity.FORWARD:
367 mActionBar.setSelectedNavigationItem(2);
368 break;
369 }
370 }
371 }
372
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800373 private void initFromRefMessage(int action, String recipientAddress) {
Mindy Pereira6349a042012-01-04 11:25:01 -0800374 ContentResolver resolver = getContentResolver();
375 Cursor refMessage = resolver.query(mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
376 null, null);
377 if (refMessage != null) {
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800378 try {
379 refMessage.moveToFirst();
380 setSubject(refMessage, action);
381 // Setup recipients
382 if (action == FORWARD) {
383 mForward = true;
384 }
385 setQuotedTextVisibility(true);
386 initRecipientsFromRefMessageCursor(recipientAddress, refMessage, action);
387 initBodyFromRefMessage(refMessage, action);
388 if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
389 updateAttachments(action, refMessage);
390 } else {
391 // Clear the attachments.
392 removeAllAttachments();
393 }
394 updateHideOrShowCcBcc();
395 } finally {
396 refMessage.close();
397 }
Mindy Pereira6349a042012-01-04 11:25:01 -0800398 }
Mindy Pereirac17d0732011-12-29 10:46:19 -0800399 }
400
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800401 private void initBodyFromRefMessage(Cursor refMessage, int action) {
402 boolean forward = action == FORWARD;
403 DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
404 Date date = new Date(refMessage.getLong(UIProvider.MESSAGE_DATE_RECEIVED_MS_COLUMN));
405 StringBuffer quotedText = new StringBuffer();
406
407 if (action == ComposeActivity.REPLY || action == ComposeActivity.REPLY_ALL) {
408 quotedText.append(QUOTE_BEGIN);
409 quotedText
410 .append(String.format(
411 getString(R.string.reply_attribution),
412 dateFormat.format(date),
413 Utils.cleanUpString(
414 refMessage.getString(UIProvider.MESSAGE_FROM_COLUMN), true)));
415 quotedText.append(HEADER_SEPARATOR);
416 quotedText.append(BLOCKQUOTE_BEGIN);
417 quotedText.append(refMessage.getString(UIProvider.MESSAGE_BODY_HTML));
418 quotedText.append(BLOCKQUOTE_END);
419 quotedText.append(QUOTE_END);
420 } else if (action == ComposeActivity.FORWARD) {
421 quotedText.append(QUOTE_BEGIN);
422 quotedText
423 .append(String.format(getString(R.string.forward_attribution), Utils
424 .cleanUpString(refMessage.getString(UIProvider.MESSAGE_FROM_COLUMN),
425 true /* remove empty quotes */), dateFormat.format(date), Utils
426 .cleanUpString(refMessage.getString(UIProvider.MESSAGE_SUBJECT_COLUMN),
427 false /* don't remove empty quotes */), Utils.cleanUpString(
428 refMessage.getString(UIProvider.MESSAGE_TO_COLUMN), true)));
429 String ccAddresses = refMessage.getString(UIProvider.MESSAGE_CC_COLUMN);
430 quotedText.append(String.format(getString(R.string.cc_attribution),
431 Utils.cleanUpString(ccAddresses, true /* remove empty quotes */)));
432 }
433 quotedText.append(HEADER_SEPARATOR);
434 quotedText.append(refMessage.getString(UIProvider.MESSAGE_BODY_HTML));
435 quotedText.append(QUOTE_END);
436 setQuotedText(quotedText.toString(), !forward);
437 }
438
439 /**
440 * Fill the quoted text WebView. There is no point in having a "Show quoted
441 * text" checkbox in a forwarded message so make sure mForward is
442 * initialized properly before calling this method so we can hide it.
443 */
444 public void setQuotedText(CharSequence text, boolean allow) {
445 // There is no way to retrieve this string from the WebView once it's
446 // been loaded, so we need to store it here.
447 mQuotedTextView.setQuotedText(text);
448 mQuotedTextView.allowQuotedText(allow);
449 // If there is quoted text, we always allow respond inline, since this
450 // may be a forward.
451 mQuotedTextView.allowRespondInline(true);
452 }
453
454 private void updateHideOrShowCcBcc() {
455 // TODO
456 }
457
458 public void removeAllAttachments() {
459 mAttachmentsView.removeAllViews();
460 }
461
462 private void updateAttachments(int action, Cursor refMessage) {
463 // TODO: when we hook up attachments, make this work properly.
464 }
465
466 private void initRecipientsFromRefMessageCursor(String recipientAddress, Cursor refMessage,
467 int action) {
468 // TODO
469
470 }
471
472 private void setSubject(Cursor refMessage, int action) {
473 String subject = refMessage.getString(UIProvider.MESSAGE_SUBJECT_COLUMN);
474 String prefix;
475 String correctedSubject = null;
476 if (action == ComposeActivity.COMPOSE) {
477 prefix = "";
478 } else if (action == ComposeActivity.FORWARD) {
479 prefix = getString(R.string.forward_subject_label);
480 } else {
481 prefix = getString(R.string.reply_subject_label);
482 }
483
484 // Don't duplicate the prefix
485 if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
486 correctedSubject = subject;
487 } else {
488 correctedSubject = String
489 .format(getString(R.string.formatted_subject), prefix, subject);
490 }
491 mSubject.setText(correctedSubject);
492 }
493
Mindy Pereirac17d0732011-12-29 10:46:19 -0800494 private RecipientEditTextView setupRecipients(int id) {
495 RecipientEditTextView view = (RecipientEditTextView) findViewById(id);
496 view.setAdapter(new RecipientAdapter(this, mAccount));
497 view.setTokenizer(new Rfc822Tokenizer());
498 if (mRecipientValidator == null) {
499 int offset = mAccount.indexOf("@") + 1;
500 String account = mAccount;
501 if (offset > -1) {
502 account = account.substring(mAccount.indexOf("@") + 1);
503 }
504 mRecipientValidator = new Rfc822Validator(account);
505 }
506 view.setValidator(mRecipientValidator);
507 return view;
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800508 }
509
510 @Override
511 public void onClick(View v) {
512 int id = v.getId();
513 switch (id) {
514 case R.id.add_cc:
515 case R.id.add_bcc:
516 // Verify that cc/ bcc aren't showing.
517 // Animate in cc/bcc.
518 mCcBccView.show();
519 break;
520 }
521 }
Mindy Pereirab47f3e22011-12-13 14:25:04 -0800522
523 @Override
524 public boolean onCreateOptionsMenu(Menu menu) {
525 super.onCreateOptionsMenu(menu);
526 MenuInflater inflater = getMenuInflater();
527 inflater.inflate(R.menu.compose_menu, menu);
528 return true;
529 }
530
531 @Override
532 public boolean onOptionsItemSelected(MenuItem item) {
533 int id = item.getItemId();
534 boolean handled = false;
535 switch (id) {
Mindy Pereira7b56a612011-12-14 12:32:28 -0800536 case R.id.add_attachment:
537 MockAttachment attachment = new MockAttachment();
538 attachment.partId = "0";
539 attachment.name = "testattachment.png";
540 attachment.contentType = MimeType.inferMimeType(attachment.name, null);
541 attachment.originExtras = "";
542 mAttachmentsView.addAttachment(attachment);
543 break;
Mindy Pereirab47f3e22011-12-13 14:25:04 -0800544 case R.id.add_cc:
545 case R.id.add_bcc:
546 mCcBccView.show();
547 handled = true;
548 break;
549 }
550 return !handled ? super.onOptionsItemSelected(item) : handled;
551 }
Mindy Pereira326c6602012-01-04 15:32:42 -0800552
553 @Override
554 public boolean onNavigationItemSelected(int position, long itemId) {
555 if (position == ComposeActivity.REPLY) {
556 mComposeMode = ComposeActivity.REPLY;
557 } else if (position == ComposeActivity.REPLY_ALL) {
558 mComposeMode = ComposeActivity.REPLY_ALL;
559 } else if (position == ComposeActivity.FORWARD) {
560 mComposeMode = ComposeActivity.FORWARD;
561 }
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800562 initFromRefMessage(mComposeMode, mAccount);
Mindy Pereira326c6602012-01-04 15:32:42 -0800563 return true;
564 }
565
566 private class ComposeModeAdapter extends ArrayAdapter<String> {
567
568 private LayoutInflater mInflater;
569
570 public ComposeModeAdapter(Context context) {
571 super(context, R.layout.compose_mode_item, R.id.mode, getResources()
572 .getStringArray(R.array.compose_modes));
573 }
574
575 private LayoutInflater getInflater() {
576 if (mInflater == null) {
577 mInflater = LayoutInflater.from(getContext());
578 }
579 return mInflater;
580 }
581
582 @Override
583 public View getView(int position, View convertView, ViewGroup parent) {
584 if (convertView == null) {
585 convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
586 }
587 ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
588 return super.getView(position, convertView, parent);
589 }
590 }
Mindy Pereira46ce0b12012-01-05 10:32:15 -0800591
592 @Override
593 public void onRespondInline(String text) {
594 appendToBody(text, false);
595 }
596
597 /**
598 * Append text to the body of the message. If there is no existing body
599 * text, just sets the body to text.
600 *
601 * @param text
602 * @param withSignature True to append a signature.
603 */
604 public void appendToBody(CharSequence text, boolean withSignature) {
605 Editable bodyText = mBodyText.getEditableText();
606 if (bodyText != null && bodyText.length() > 0) {
607 bodyText.append(text);
608 } else {
609 setBody(text, withSignature);
610 }
611 }
612
613 /**
614 * Set the body of the message.
615 * @param text
616 * @param withSignature True to append a signature.
617 */
618 public void setBody(CharSequence text, boolean withSignature) {
619 mBodyText.setText(text);
620 }
Mindy Pereira1a95a572012-01-05 12:21:29 -0800621
622 @Override
623 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
624 // TODO
625 }
626
627 @Override
628 public void onNothingSelected(AdapterView<?> parent) {
629 // Do nothing.
630 }
Mindy Pereira8e9305e2011-12-13 14:25:04 -0800631}