Fix reply all behavior to prevent duplicates, etc.

* Also create a bogus account if none exists to prevent setup failure
* Fixes #2087413 (Email large tests failing with Unknown URI)
* Fixes #2097361 (Duplicate email address when replying...)

Change-Id: I9ca2ac5f98db43e9009c22421c69956440356e34
This commit is contained in:
Marc Blank 2009-09-03 16:37:41 -07:00
parent 5d09beeacb
commit b8d0c55a05
2 changed files with 357 additions and 158 deletions

View File

@ -18,10 +18,8 @@ package com.android.email.activity;
import com.android.email.Controller;
import com.android.email.Email;
import com.android.email.EmailAddressAdapter;
import com.android.email.EmailAddressValidator;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.mail.MessagingException;
import com.android.email.mail.internet.EmailHtmlUtil;
@ -69,7 +67,6 @@ import android.widget.MultiAutoCompleteTextView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
@ -263,8 +260,8 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
final String action = intent.getAction();
// Handle the various intents that launch the message composer
if (Intent.ACTION_VIEW.equals(action)
|| Intent.ACTION_SENDTO.equals(action)
if (Intent.ACTION_VIEW.equals(action)
|| Intent.ACTION_SENDTO.equals(action)
|| Intent.ACTION_SEND.equals(action)
|| Intent.ACTION_SEND_MULTIPLE.equals(action)) {
setAccount(intent);
@ -282,7 +279,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mLoadAttachmentsTask = new AsyncTask<Long, Void, Attachment[]>() {
@Override
protected Attachment[] doInBackground(Long... messageIds) {
return Attachment.restoreAttachmentsWithMessageId(MessageCompose.this,
return Attachment.restoreAttachmentsWithMessageId(MessageCompose.this,
messageIds[0]);
}
@Override
@ -432,6 +429,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
if (!dotFound) {
return null;
}
// we have found a comma-insert case. now just do it
// in the least expensive way we can.
if (source instanceof Spanned) {
@ -472,10 +470,10 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mQuotedTextDelete.setOnClickListener(this);
EmailAddressAdapter addressAdapter = new EmailAddressAdapter(this);
// Temporarily disable addressAdapter, see BUG 2077496
// EmailAddressAdapter addressAdapter = new EmailAddressAdapter(this);
EmailAddressValidator addressValidator = new EmailAddressValidator();
// temporarilly disable setAdapter, see BUG 2077496
// mToView.setAdapter(addressAdapter);
mToView.setTokenizer(new Rfc822Tokenizer());
mToView.setValidator(addressValidator);
@ -550,7 +548,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
addAddress(view, address.toString());
}
}
private void addAddresses(MultiAutoCompleteTextView view, String[] addresses) {
if (addresses == null) {
return;
@ -744,7 +742,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
sendOrSaveMessage(false);
}
/**
/**
* Checks whether all the email addresses listed in TO, CC, BCC are valid.
*/
/* package */ boolean isAddressAllValid() {
@ -898,7 +896,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
break;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -963,22 +961,22 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// return true;
// }
// }
/**
* Fill all the widgets with the content found in the Intent Extra, if any.
*
* Fill all the widgets with the content found in the Intent Extra, if any.
*
* Note that we don't actually check the intent action (typically VIEW, SENDTO, or SEND).
* There is enough overlap in the definitions that it makes more sense to simply check for
* all available data and use as much of it as possible.
*
*
* With one exception: EXTRA_STREAM is defined as only valid for ACTION_SEND.
*
*
* @param intent the launch intent
*/
/* package */ void initFromIntent(Intent intent) {
// First, add values stored in top-level extras
String[] extraStrings = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
if (extraStrings != null) {
addAddresses(mToView, extraStrings);
@ -1000,7 +998,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// We'll take two courses here. If it's mailto:, there is a specific set of rules
// that define various optional fields. However, for any other scheme, we'll simply
// take the entire scheme-specific part and interpret it as a possible list of addresses.
final Uri dataUri = intent.getData();
if (dataUri != null) {
if ("mailto".equals(dataUri.getScheme())) {
@ -1014,14 +1012,14 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
// Next, fill in the plaintext (note, this will override mailto:?body=)
CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
if (text != null) {
mMessageContentView.setText(text);
}
// Next, convert EXTRA_STREAM into an attachment
if (Intent.ACTION_SEND.equals(intent.getAction()) && intent.hasExtra(Intent.EXTRA_STREAM)) {
String type = intent.getType();
Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
@ -1032,7 +1030,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())
&& intent.hasExtra(Intent.EXTRA_STREAM)) {
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (list != null) {
@ -1040,7 +1038,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
Uri uri = (Uri) parcelable;
if (uri != null) {
Attachment attachment = loadAttachmentInfo(uri);
if (MimeUtility.mimeTypeMatches(attachment.mMimeType,
if (MimeUtility.mimeTypeMatches(attachment.mMimeType,
Email.ACCEPTABLE_ATTACHMENT_SEND_TYPES)) {
addAttachment(attachment);
}
@ -1048,9 +1046,9 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
}
// Finally - expose fields that were filled in but are normally hidden, and set focus
if (mCcView.length() > 0) {
mCcView.setVisibility(View.VISIBLE);
}
@ -1064,14 +1062,14 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
/**
* When we are launched with an intent that includes a mailto: URI, we can actually
* gather quite a few of our message fields from it.
*
*
* @mailToString the href (which must start with "mailto:").
*/
private void initializeFromMailTo(String mailToString) {
// Chop up everything between mailto: and ? to find recipients
int index = mailToString.indexOf("?");
int length = "mailto".length() + 1;
int length = "mailto".length() + 1;
String to;
try {
// Extract the recipient after mailto:
@ -1121,7 +1119,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
if (text != null) {
text = plainTextFlag ? EmailHtmlUtil.escapeCharacterToDisplay(text) : text;
// TODO: re-enable EmailHtmlUtil.resolveInlineImage() for HTML
// EmailHtmlUtil.resolveInlineImage(getContentResolver(), mAccount,
// EmailHtmlUtil.resolveInlineImage(getContentResolver(), mAccount,
// text, message, 0);
mQuotedTextBar.setVisibility(View.VISIBLE);
mQuotedText.setVisibility(View.VISIBLE);
@ -1130,6 +1128,62 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
/**
* Given a packed address String, the address of our sending account, a view, and a list of
* addressees already added to other addressing views, adds unique addressees that don't
* match our address to the passed in view
*/
private boolean safeAddAddresses(String addrs, String ourAddress,
MultiAutoCompleteTextView view, ArrayList<Address> addrList) {
boolean added = false;
for (Address address : Address.unpack(addrs)) {
// Don't send to ourselves or already-included addresses
if (!address.getAddress().equalsIgnoreCase(ourAddress) && !addrList.contains(address)) {
addrList.add(address);
addAddress(view, address.toString());
added = true;
}
}
return added;
}
/**
* Set up the to and cc views properly for the "reply" and "replyAll" cases. What's important
* is that we not 1) send to ourselves, and 2) duplicate addressees.
* @param message the message we're replying to
* @param account the account we're sending from
* @param toView the "To" view
* @param ccView the "Cc" view
* @param replyAll whether this is a replyAll (vs a reply)
*/
/*package*/ void setupAddressViews(Message message, Account account,
MultiAutoCompleteTextView toView, MultiAutoCompleteTextView ccView, boolean replyAll) {
/*
* If a reply-to was included with the message use that, otherwise use the from
* or sender address.
*/
Address[] replyToAddresses = Address.unpack(message.mReplyTo);
if (replyToAddresses.length == 0) {
replyToAddresses = Address.unpack(message.mFrom);
}
addAddresses(mToView, replyToAddresses);
if (replyAll) {
// Keep a running list of addresses we're sending to
ArrayList<Address> allAddresses = new ArrayList<Address>();
String ourAddress = account.mEmailAddress;
for (Address address: replyToAddresses) {
allAddresses.add(address);
}
safeAddAddresses(message.mTo, ourAddress, mToView, allAddresses);
if (safeAddAddresses(message.mCc, ourAddress, mCcView, allAddresses)) {
mCcView.setVisibility(View.VISIBLE);
}
}
}
/**
* Pull out the parts of the now loaded source message and apply them to the new message
* depending on the type of message being composed.
@ -1141,39 +1195,14 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mDraftNeedsSaving = true;
final String subject = message.mSubject;
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) {
setupAddressViews(message, account, mToView, mCcView, ACTION_REPLY_ALL.equals(action));
if (subject != null && !subject.toLowerCase().startsWith("re:")) {
mSubjectView.setText("Re: " + subject);
} else {
mSubjectView.setText(subject);
}
/*
* If a reply-to was included with the message use that, otherwise use the from
* or sender address.
*/
Address[] replyToAddresses = Address.unpack(message.mReplyTo);
if (replyToAddresses.length == 0) {
replyToAddresses = Address.unpack(message.mFrom);
}
addAddresses(mToView, replyToAddresses);
if (ACTION_REPLY_ALL.equals(action)) {
for (Address address : Address.unpack(message.mTo)) {
if (!address.getAddress().equalsIgnoreCase(account.mEmailAddress)) {
addAddress(mToView, address.toString());
}
}
boolean makeCCVisible = false;
for (Address address : Address.unpack(message.mCc)) {
if (!Utility.arrayContains(replyToAddresses, address)) {
addAddress(mCcView, address.toString());
makeCCVisible = true;
}
}
if (makeCCVisible) {
mCcView.setVisibility(View.VISIBLE);
}
}
displayQuotedText(message);
} else if (ACTION_FORWARD.equals(action)) {
mSubjectView.setText(subject != null && !subject.toLowerCase().startsWith("fwd:") ?
@ -1213,7 +1242,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
/**
* In order to accelerate typing, position the cursor in the first empty field,
* In order to accelerate typing, position the cursor in the first empty field,
* or at the end of the body composition field if none are empty. Typically, this will
* play out as follows:
* Reply / Reply All - put cursor in the empty message body
@ -1227,7 +1256,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mSubjectView.requestFocus();
} else {
mMessageContentView.requestFocus();
// when selecting the message content, explicitly move IP to the end, so you can
// when selecting the message content, explicitly move IP to the end, so you can
// quickly resume typing into a draft
int selection = mMessageContentView.length();
mMessageContentView.setSelection(selection, selection);

View File

@ -17,41 +17,40 @@
package com.android.email.activity;
import com.android.email.Email;
import com.android.email.R;
import com.android.email.EmailAddressValidator;
import com.android.email.R;
import com.android.email.mail.Address;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.internet.MimeMessage;
import com.android.email.mail.internet.TextBody;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Message;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;
import android.widget.EditText;
import android.widget.AutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView;
/**
* Various instrumentation tests for MessageCompose.
*
* Various instrumentation tests for MessageCompose.
*
* It might be possible to convert these to ActivityUnitTest, which would be faster.
*/
@LargeTest
public class MessageComposeInstrumentationTests
public class MessageComposeInstrumentationTests
extends ActivityInstrumentationTestCase2<MessageCompose> {
private AutoCompleteTextView mToView;
private MultiAutoCompleteTextView mToView;
private MultiAutoCompleteTextView mCcView;
private EditText mSubjectView;
private EditText mMessageView;
private long mCreatedAccountId = -1;
private static final String SENDER = "sender@android.com";
private static final String REPLYTO = "replyto@android.com";
private static final String RECIPIENT_TO = "recipient-to@android.com";
@ -62,28 +61,37 @@ public class MessageComposeInstrumentationTests
private static final String REPLY_BODY_SHORT = "\n\n" + SENDER + " wrote:\n\n";
private static final String REPLY_BODY = REPLY_BODY_SHORT + ">" + BODY;
private static final String FROM = "Fred From <from@google.com>";
private static final String TO1 = "First To <first.to@google.com>";
private static final String TO2 = "Second To <second.to@google.com>";
private static final String TO3 = "CopyFirst Cc <first.cc@google.com>";
private static final String CC1 = "First Cc <first.cc@google.com>";
private static final String CC2 = "Second Cc <second.cc@google.com>";
private static final String CC3 = "Third Cc <third.cc@google.com>";
private static final String CC4 = "CopySecond To <second.to@google.com>";
private static final String UTF16_SENDER =
"\u3042\u3044\u3046 \u3048\u304A <sender@android.com>";
private static final String UTF16_REPLYTO =
private static final String UTF16_REPLYTO =
"\u3042\u3044\u3046\u3048\u304A <replyto@android.com>";
private static final String UTF16_RECIPIENT_TO =
private static final String UTF16_RECIPIENT_TO =
"\"\u3042\u3044\u3046,\u3048\u304A\" <recipient-to@android.com>";
private static final String UTF16_RECIPIENT_CC =
private static final String UTF16_RECIPIENT_CC =
"\u30A2\u30AB \u30B5\u30BF\u30CA <recipient-cc@android.com>";
private static final String UTF16_RECIPIENT_BCC =
private static final String UTF16_RECIPIENT_BCC =
"\"\u30A2\u30AB,\u30B5\u30BF\u30CA\" <recipient-bcc@android.com>";
private static final String UTF16_SUBJECT = "\u304A\u5BFF\u53F8\u306B\u3059\u308B\uFF1F";
private static final String UTF16_BODY = "\u65E5\u672C\u8A9E\u306E\u6587\u7AE0";
private static final String UTF32_SENDER =
"\uD834\uDF01\uD834\uDF46 \uD834\uDF22 <sender@android.com>";
private static final String UTF32_REPLYTO =
private static final String UTF32_REPLYTO =
"\uD834\uDF01\uD834\uDF46\uD834\uDF22 <replyto@android.com>";
private static final String UTF32_RECIPIENT_TO =
private static final String UTF32_RECIPIENT_TO =
"\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <recipient-to@android.com>";
private static final String UTF32_RECIPIENT_CC =
private static final String UTF32_RECIPIENT_CC =
"\uD834\uDF22 \uD834\uDF01\uD834\uDF46 <recipient-cc@android.com>";
private static final String UTF32_RECIPIENT_BCC =
private static final String UTF32_RECIPIENT_BCC =
"\"\uD834\uDF22,\uD834\uDF01\uD834\uDF46\" <recipient-bcc@android.com>";
private static final String UTF32_SUBJECT = "\uD834\uDF01\uD834\uDF46";
private static final String UTF32_BODY = "\uD834\uDF01\uD834\uDF46";
@ -93,7 +101,7 @@ public class MessageComposeInstrumentationTests
private static final String ACTION_REPLY_ALL = "com.android.email.intent.action.REPLY_ALL";
private static final String ACTION_FORWARD = "com.android.email.intent.action.FORWARD";
private static final String ACTION_EDIT_DRAFT = "com.android.email.intent.action.EDIT_DRAFT";
public MessageComposeInstrumentationTests() {
super("com.android.email", MessageCompose.class);
}
@ -107,20 +115,40 @@ public class MessageComposeInstrumentationTests
protected void setUp() throws Exception {
super.setUp();
Context context = getInstrumentation().getTargetContext();
// Force assignment of a default account
long accountId = Account.getDefaultAccountId(context);
if (accountId == -1) {
Account account = new Account();
account.mSenderName = "Bob Sender";
account.mEmailAddress = "bob@sender.com";
account.save(context);
accountId = account.mId;
mCreatedAccountId = accountId;
}
Account.restoreAccountWithId(context, accountId);
Email.setServicesEnabled(context);
Intent intent = new Intent(Intent.ACTION_VIEW);
setActivityIntent(intent);
final MessageCompose a = getActivity();
mToView = (AutoCompleteTextView) a.findViewById(R.id.to);
mToView = (MultiAutoCompleteTextView) a.findViewById(R.id.to);
mCcView = (MultiAutoCompleteTextView) a.findViewById(R.id.cc);
mSubjectView = (EditText) a.findViewById(R.id.subject);
mMessageView = (EditText) a.findViewById(R.id.message_content);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
Context context = getInstrumentation().getTargetContext();
// If we created an account, delete it here
if (mCreatedAccountId > -1) {
context.getContentResolver().delete(
ContentUris.withAppendedId(Account.CONTENT_URI, mCreatedAccountId), null, null);
}
}
/**
* The name 'test preconditions' is a convention to signal that if this
* test doesn't pass, the test case was not set up properly and it might
@ -135,7 +163,7 @@ public class MessageComposeInstrumentationTests
assertNotNull(mMessageView);
assertEquals(0, mMessageView.length());
}
/**
* Test for buildBodyText().
* Compare with expected values.
@ -145,7 +173,7 @@ public class MessageComposeInstrumentationTests
final Message message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
Intent intent = new Intent(ACTION_REPLY);
final MessageCompose a = getActivity();
a.setIntent(intent);
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
@ -171,16 +199,16 @@ public class MessageComposeInstrumentationTests
* To = (Reply-To or From:) + To: + Cc: (if REPLY_ALL)
* Subject = Re: Subject
* Body = empty (and has cursor)
*
*
* TODO test REPLY_ALL
*/
public void testProcessSourceMessageReply() throws MessagingException, Throwable {
final Message message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
Intent intent = new Intent(ACTION_REPLY);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -188,10 +216,10 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
message.mFrom = null;
message.mReplyTo = Address.parseAndPack(REPLYTO);
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -201,7 +229,7 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test reply to utf-16 name and address
*/
@ -211,7 +239,7 @@ public class MessageComposeInstrumentationTests
Intent intent = new Intent(ACTION_REPLY);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -219,10 +247,10 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
message.mFrom = null;
message.mReplyTo = Address.parseAndPack(UTF16_REPLYTO);
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -232,7 +260,7 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test reply to utf-32 name and address
*/
@ -242,7 +270,7 @@ public class MessageComposeInstrumentationTests
Intent intent = new Intent(ACTION_REPLY);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -250,10 +278,10 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
message.mFrom = null;
message.mReplyTo = Address.parseAndPack(UTF32_REPLYTO);
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -263,19 +291,19 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test processSourceMessage() for FORWARD
* To = empty (and has cursor)
* Subject = Fwd: Subject
* Body = empty
*/
public void testProcessSourceMessageForward() throws MessagingException, Throwable {
public void testProcessSourceMessageForward() throws MessagingException, Throwable {
final Message message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
Intent intent = new Intent(ACTION_FORWARD);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -284,23 +312,23 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test processSourceMessage() for EDIT_DRAFT
* Reply and ReplyAll should map:
* To = to
* Subject = Subject
* Body = body (has cursor)
*
*
* TODO check CC and BCC handling too
*/
public void testProcessSourceMessageDraft() throws MessagingException, Throwable {
final Message message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
Intent intent = new Intent(ACTION_EDIT_DRAFT);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -308,11 +336,11 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
// if subject is null, then cursor should be there instead
message.mSubject = "";
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -321,21 +349,21 @@ public class MessageComposeInstrumentationTests
checkFocused(mSubjectView);
}
});
}
/**
* Test processSourceMessage() for EDIT_DRAFT with utf-16 name and address
* TODO check CC and BCC handling too
*/
public void testProcessSourceMessageDraftWithUtf16() throws MessagingException, Throwable {
final Message message = buildTestMessage(UTF16_RECIPIENT_TO, UTF16_SENDER,
UTF16_SUBJECT, UTF16_BODY);
Intent intent = new Intent(ACTION_EDIT_DRAFT);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -344,11 +372,11 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
// if subject is null, then cursor should be there instead
message.mSubject = "";
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -357,7 +385,7 @@ public class MessageComposeInstrumentationTests
checkFocused(mSubjectView);
}
});
}
/**
@ -365,13 +393,13 @@ public class MessageComposeInstrumentationTests
* TODO check CC and BCC handling too
*/
public void testProcessSourceMessageDraftWithUtf32() throws MessagingException, Throwable {
final Message message = buildTestMessage(UTF32_RECIPIENT_TO, UTF32_SENDER,
UTF32_SUBJECT, UTF32_BODY);
Intent intent = new Intent(ACTION_EDIT_DRAFT);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
@ -380,11 +408,11 @@ public class MessageComposeInstrumentationTests
checkFocused(mMessageView);
}
});
// if subject is null, then cursor should be there instead
message.mSubject = "";
runTestOnUiThread(new Runnable() {
public void run() {
resetViews();
@ -393,7 +421,149 @@ public class MessageComposeInstrumentationTests
checkFocused(mSubjectView);
}
});
}
/**
* Check that we create the proper to and cc addressees in reply and reply-all, making sure
* to reject duplicate addressees AND the email address of the sending account
*
* In this case, we're doing a "reply"
* The user is TO1 (a "to" recipient)
* The to should be: FROM
* The cc should be empty
*/
public void testReplyAddresses() throws Throwable {
final MessageCompose a = getActivity();
// Doesn't matter what Intent we use here
final Intent intent = new Intent(Intent.ACTION_VIEW);
Message msg = new Message();
final Account account = new Account();
msg.mFrom = Address.parseAndPack(FROM);
msg.mTo = Address.parseAndPack(TO1 + ',' + TO2);
msg.mCc = Address.parseAndPack(CC1 + ',' + CC2 + ',' + CC3);
final Message message = msg;
account.mEmailAddress = "FiRsT.tO@gOoGlE.cOm";
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(intent);
a.setupAddressViews(message, account, mToView, mCcView, false);
assertEquals("", mCcView.getText().toString());
String result = Address.parseAndPack(mToView.getText().toString());
String expected = Address.parseAndPack(FROM);
assertEquals(expected, result);
}
});
}
/**
* Check that we create the proper to and cc addressees in reply and reply-all, making sure
* to reject duplicate addressees AND the email address of the sending account
*
* In this case, we're doing a "reply all"
* The user is TO1 (a "to" recipient)
* The to should be: FROM and TO2
* The cc should be: CC1, CC2, and CC3
*/
public void testReplyAllAddresses1() throws Throwable {
final MessageCompose a = getActivity();
// Doesn't matter what Intent we use here
final Intent intent = new Intent(Intent.ACTION_VIEW);
Message msg = new Message();
final Account account = new Account();
msg.mFrom = Address.parseAndPack(FROM);
msg.mTo = Address.parseAndPack(TO1 + ',' + TO2);
msg.mCc = Address.parseAndPack(CC1 + ',' + CC2 + ',' + CC3);
final Message message = msg;
account.mEmailAddress = "FiRsT.tO@gOoGlE.cOm";
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(intent);
a.setupAddressViews(message, account, mToView, mCcView, true);
String result = Address.parseAndPack(mToView.getText().toString());
String expected = Address.parseAndPack(FROM + ',' + TO2);
assertEquals(expected, result);
result = Address.parseAndPack(mCcView.getText().toString());
expected = Address.parseAndPack(CC1 + ',' + CC2 + ',' + CC3);
assertEquals(expected, result);
}
});
}
/**
* Check that we create the proper to and cc addressees in reply and reply-all, making sure
* to reject duplicate addressees AND the email address of the sending account
*
* In this case, we're doing a "reply all"
* The user is CC2 (a "cc" recipient)
* The to should be: FROM, TO1, and TO2
* The cc should be: CC1 and CC3 (CC2 is our account's email address)
*/
public void testReplyAllAddresses2() throws Throwable {
final MessageCompose a = getActivity();
// Doesn't matter what Intent we use here
final Intent intent = new Intent(Intent.ACTION_VIEW);
Message msg = new Message();
final Account account = new Account();
msg.mFrom = Address.parseAndPack(FROM);
msg.mTo = Address.parseAndPack(TO1 + ',' + TO2);
msg.mCc = Address.parseAndPack(CC1 + ',' + CC2 + ',' + CC3);
final Message message = msg;
account.mEmailAddress = "sEcOnD.cC@gOoGlE.cOm";
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(intent);
a.setupAddressViews(message, account, mToView, mCcView, true);
String result = Address.parseAndPack(mToView.getText().toString());
String expected = Address.parseAndPack(FROM + ',' + TO1 + ',' + TO2);
assertEquals(expected, result);
result = Address.parseAndPack(mCcView.getText().toString());
expected = Address.parseAndPack(CC1 + ',' + CC3);
assertEquals(expected, result);
}
});
}
/**
* Check that we create the proper to and cc addressees in reply and reply-all, making sure
* to reject duplicate addressees AND the email address of the sending account
*
* In this case, we're doing a "reply all"
* The user is CC2 (a "cc" recipient)
* The to should be: FROM, TO1, TO2, and TO3
* The cc should be: CC3 (CC1/CC4 are duplicates; CC2 is the our account's email address)
*/
public void testReplyAllAddresses3() throws Throwable {
final MessageCompose a = getActivity();
// Doesn't matter what Intent we use here
final Intent intent = new Intent(Intent.ACTION_VIEW);
Message msg = new Message();
final Account account = new Account();
msg.mFrom = Address.parseAndPack(FROM);
msg.mTo = Address.parseAndPack(TO1 + ',' + TO2 + ',' + TO3);
msg.mCc = Address.parseAndPack(CC1 + ',' + CC2 + ',' + CC3 + ',' + CC4);
final Message message = msg;
account.mEmailAddress = "sEcOnD.cC@gOoGlE.cOm";
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(intent);
a.setupAddressViews(message, account, mToView, mCcView, true);
String result = Address.parseAndPack(mToView.getText().toString());
String expected = Address.parseAndPack(FROM + ',' + TO1 + ',' + TO2 + ',' + TO3);
assertEquals(expected, result);
result = Address.parseAndPack(mCcView.getText().toString());
expected = Address.parseAndPack(CC3);
assertEquals(expected, result);
}
});
}
/**
@ -401,16 +571,16 @@ public class MessageComposeInstrumentationTests
* Intent.EXTRA_EMAIL, Intent.EXTRA_CC, Intent.EXTRA_BCC, Intent.EXTRA_SUBJECT
*/
public void testIntentHeaderExtras() throws MessagingException, Throwable {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { RECIPIENT_TO });
intent.putExtra(Intent.EXTRA_CC, new String[] { RECIPIENT_CC });
intent.putExtra(Intent.EXTRA_BCC, new String[] { RECIPIENT_BCC });
intent.putExtra(Intent.EXTRA_SUBJECT, SUBJECT);
final MessageCompose a = getActivity();
final Intent i2 = new Intent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(i2);
@ -419,21 +589,21 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test for processing of Intent EXTRA_* fields that impact the headers with utf-16.
*/
public void testIntentHeaderExtrasWithUtf16() throws MessagingException, Throwable {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { UTF16_RECIPIENT_TO });
intent.putExtra(Intent.EXTRA_CC, new String[] { UTF16_RECIPIENT_CC });
intent.putExtra(Intent.EXTRA_BCC, new String[] { UTF16_RECIPIENT_BCC });
intent.putExtra(Intent.EXTRA_SUBJECT, UTF16_SUBJECT);
final MessageCompose a = getActivity();
final Intent i2 = new Intent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(i2);
@ -448,16 +618,16 @@ public class MessageComposeInstrumentationTests
* Test for processing of Intent EXTRA_* fields that impact the headers with utf-32.
*/
public void testIntentHeaderExtrasWithUtf32() throws MessagingException, Throwable {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { UTF32_RECIPIENT_TO });
intent.putExtra(Intent.EXTRA_CC, new String[] { UTF32_RECIPIENT_CC });
intent.putExtra(Intent.EXTRA_BCC, new String[] { UTF32_RECIPIENT_BCC });
intent.putExtra(Intent.EXTRA_SUBJECT, UTF32_SUBJECT);
final MessageCompose a = getActivity();
final Intent i2 = new Intent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(i2);
@ -473,14 +643,14 @@ public class MessageComposeInstrumentationTests
* type="text/plain", EXTRA_TEXT="http:link.server.com"
*/
public void testIntentSendPlainText() throws MessagingException, Throwable {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, BODY);
final MessageCompose a = getActivity();
final Intent i2 = new Intent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(i2);
@ -489,7 +659,7 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* Test for processing of a typical browser Mailto intent, e.g.
* action=android.intent.action.VIEW
@ -497,14 +667,14 @@ public class MessageComposeInstrumentationTests
* data=mailto:user@domain.com?subject=This%20is%20%the%subject
*/
public void testBrowserMailToIntent() throws MessagingException, Throwable {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("mailto:" + RECIPIENT_TO + "?subject=This%20is%20the%20subject");
intent.setData(uri);
final MessageCompose a = getActivity();
final Intent i2 = new Intent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.initFromIntent(i2);
@ -513,17 +683,17 @@ public class MessageComposeInstrumentationTests
}
});
}
/**
* TODO: test mailto: with simple encoding mode
* TODO: test mailto: URI with all optional fields
* TODO: come up with a way to add a very small attachment
* TODO: confirm the various details between handling of SEND, VIEW, SENDTO
*/
/**
* Helper method to quickly check (and assert) on the to, subject, and content views.
*
*
* @param to expected value (null = it must be empty)
* @param cc expected value (null = it must be empty)
* @param bcc expected value (null = it must be empty)
@ -552,7 +722,7 @@ public class MessageComposeInstrumentationTests
assertEquals(content, contentText);
}
}
/**
* Helper method to verify which field has the focus
* @param focused The view that should be focused (all others should not have focus)
@ -562,11 +732,11 @@ public class MessageComposeInstrumentationTests
assertEquals(focused == mSubjectView, mSubjectView.isFocused());
assertEquals(focused == mMessageView, mMessageView.isFocused());
}
/**
* Helper used when running multiple calls to processSourceMessage within a test method.
* Simply clears out the views, so that we get fresh data and not appended data.
*
*
* Must call from UI thread.
*/
private void resetViews() {
@ -574,10 +744,10 @@ public class MessageComposeInstrumentationTests
mSubjectView.setText(null);
mMessageView.setText(null);
}
/**
* Build a test message that can be used as input to processSourceMessage
*
*
* @param to Recipient(s) of the message
* @param sender Sender(s) of the message
* @param subject Subject of the message
@ -587,26 +757,26 @@ public class MessageComposeInstrumentationTests
private Message buildTestMessage(String to, String sender, String subject, String content)
throws MessagingException {
Message message = new Message();
if (to != null) {
message.mTo = Address.parseAndPack(to);
}
if (sender != null) {
Address[] addresses = Address.parse(sender);
assertTrue("from address", addresses.length > 0);
message.mFrom = addresses[0].pack();
}
message.mSubject = subject;
if (content != null) {
message.mText = content;
}
return message;
}
/**
* Check AddressTextView email address validation.
*/
@ -651,19 +821,19 @@ public class MessageComposeInstrumentationTests
checkCommaInsert("a@b", "", false);
checkCommaInsert("a@b.", "", true); // non-optimal, but matches current implementation
checkCommaInsert("a@b.c", "", true);
// confirm works properly for internal editing
checkCommaInsert("me@foo.com, you", " they@bar.com", false);
checkCommaInsert("me@foo.com, you@", "they@bar.com", false);
checkCommaInsert("me@foo.com, you@bar", " they@bar.com", false);
checkCommaInsert("me@foo.com, you@bar.", " they@bar.com", true); // non-optimal
checkCommaInsert("me@foo.com, you@bar.com", " they@bar.com", true);
// check a couple of multi-period cases
checkCommaInsert("me.myself@foo", "", false);
checkCommaInsert("me.myself@foo.com", "", true);
checkCommaInsert("me@foo.co.uk", "", true);
// cases that should not append because there's already a comma
checkCommaInsert("a@b.c,", "", false);
checkCommaInsert("me@foo.com, you@bar.com,", " they@bar.com", false);
@ -687,6 +857,6 @@ public class MessageComposeInstrumentationTests
getInstrumentation().sendStringSync(" ");
String result = mToView.getText().toString();
assertEquals(expect, result);
}
}