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:
parent
5d09beeacb
commit
b8d0c55a05
@ -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;
|
||||
@ -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);
|
||||
@ -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:") ?
|
||||
|
@ -17,26 +17,23 @@
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
@ -48,9 +45,11 @@ import android.widget.AutoCompleteTextView;
|
||||
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";
|
||||
@ -62,6 +61,15 @@ 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 =
|
||||
@ -110,17 +118,37 @@ public class MessageComposeInstrumentationTests
|
||||
|
||||
// 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
|
||||
@ -396,6 +424,148 @@ public class MessageComposeInstrumentationTests
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for processing of Intent EXTRA_* fields that impact the headers:
|
||||
* Intent.EXTRA_EMAIL, Intent.EXTRA_CC, Intent.EXTRA_BCC, Intent.EXTRA_SUBJECT
|
||||
|
Loading…
Reference in New Issue
Block a user