Add Signature to Email Composer
Bug: 1323618 - Add signature to the message - Set selection just after the message - Add tests for signature
This commit is contained in:
parent
0f42334af2
commit
2a41aa538f
|
@ -52,6 +52,7 @@ import android.provider.OpenableColumns;
|
|||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.util.Rfc822Tokenizer;
|
||||
import android.util.Log;
|
||||
|
@ -326,6 +327,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
mMessageLoaded = true;
|
||||
mSourceMessageProcessed = true;
|
||||
}
|
||||
setInitialComposeText(null, (mAccount != null) ? mAccount.mSignature : null);
|
||||
}
|
||||
|
||||
if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction) ||
|
||||
|
@ -549,6 +551,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
mSaveButton.setOnClickListener(this);
|
||||
|
||||
mSubjectView.setOnFocusChangeListener(this);
|
||||
mMessageContentView.setOnFocusChangeListener(this);
|
||||
}
|
||||
|
||||
// TODO: is there any way to unify this with MessageView.LoadMessageTask?
|
||||
|
@ -647,6 +650,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
public void onFocusChange(View view, boolean focused) {
|
||||
if (!focused) {
|
||||
updateTitle();
|
||||
} else {
|
||||
switch (view.getId()) {
|
||||
case R.id.message_content:
|
||||
setMessageContentSelection((mAccount != null) ? mAccount.mSignature : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1125,25 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Set a message body and a signature when the Activity is launched.
|
||||
*
|
||||
* @param text the message body
|
||||
*/
|
||||
/* package */ void setInitialComposeText(CharSequence text, String signature) {
|
||||
int textLength = 0;
|
||||
if (text != null) {
|
||||
mMessageContentView.append(text);
|
||||
textLength = text.length();
|
||||
}
|
||||
if (!TextUtils.isEmpty(signature)) {
|
||||
if (textLength == 0 || text.charAt(textLength - 1) != '\n') {
|
||||
mMessageContentView.append("\n");
|
||||
}
|
||||
mMessageContentView.append(signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all the widgets with the content found in the Intent Extra, if any.
|
||||
*
|
||||
|
@ -1170,7 +1197,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
|
||||
CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
|
||||
if (text != null) {
|
||||
mMessageContentView.setText(text);
|
||||
setInitialComposeText(text, null);
|
||||
}
|
||||
|
||||
// Next, convert EXTRA_STREAM into an attachment
|
||||
|
@ -1260,7 +1287,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
|
||||
List<String> body = uri.getQueryParameters("body");
|
||||
if (body.size() > 0) {
|
||||
mMessageContentView.setText(body.get(0));
|
||||
setInitialComposeText(body.get(0), (mAccount != null) ? mAccount.mSignature : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1411,6 +1438,34 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||
setNewMessageFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cursor to the end of a body except a signature
|
||||
*/
|
||||
/* package */ void setMessageContentSelection(String signature) {
|
||||
// when selecting the message content, explicitly move IP to the end of the message,
|
||||
// so you can quickly resume typing into a draft
|
||||
int selection = mMessageContentView.length();
|
||||
if (!TextUtils.isEmpty(signature)) {
|
||||
int signatureLength = signature.length();
|
||||
int estimatedSelection = selection - signatureLength;
|
||||
if (estimatedSelection >= 0) {
|
||||
CharSequence text = mMessageContentView.getText();
|
||||
int i = 0;
|
||||
while (i < signatureLength
|
||||
&& text.charAt(estimatedSelection + i) == signature.charAt(i)) {
|
||||
++i;
|
||||
}
|
||||
if (i == signatureLength) {
|
||||
selection = estimatedSelection;
|
||||
while (selection > 0 && text.charAt(selection - 1) == '\n') {
|
||||
--selection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mMessageContentView.setSelection(selection, selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1426,10 +1481,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
|
||||
// quickly resume typing into a draft
|
||||
int selection = mMessageContentView.length();
|
||||
mMessageContentView.setSelection(selection, selection);
|
||||
setMessageContentSelection((mAccount != null) ? mAccount.mSignature : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ public class MessageComposeInstrumentationTests
|
|||
private EditText mSubjectView;
|
||||
private EditText mMessageView;
|
||||
private long mCreatedAccountId = -1;
|
||||
private String mSignature;
|
||||
|
||||
private static final String SENDER = "sender@android.com";
|
||||
private static final String REPLYTO = "replyto@android.com";
|
||||
|
@ -60,6 +61,7 @@ public class MessageComposeInstrumentationTests
|
|||
private static final String BODY = "This is the body. This is also the body.";
|
||||
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 SIGNATURE = "signature";
|
||||
|
||||
private static final String FROM = "Fred From <from@google.com>";
|
||||
private static final String TO1 = "First To <first.to@google.com>";
|
||||
|
@ -126,7 +128,8 @@ public class MessageComposeInstrumentationTests
|
|||
accountId = account.mId;
|
||||
mCreatedAccountId = accountId;
|
||||
}
|
||||
Account.restoreAccountWithId(context, accountId);
|
||||
Account account = Account.restoreAccountWithId(context, accountId);
|
||||
mSignature = account.getSignature();
|
||||
Email.setServicesEnabled(context);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
|
@ -183,7 +186,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(SENDER + ", ", null, null, "Re: " + SUBJECT, null);
|
||||
checkFields(SENDER + ", ", null, null, "Re: " + SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -195,7 +198,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(REPLYTO + ", ", null, null, "Re: " + SUBJECT, null);
|
||||
checkFields(REPLYTO + ", ", null, null, "Re: " + SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -214,7 +217,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF16_SENDER + ", ", null, null, "Re: " + UTF16_SUBJECT, null);
|
||||
checkFields(UTF16_SENDER + ", ", null, null, "Re: " + UTF16_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -226,7 +229,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF16_REPLYTO + ", ", null, null, "Re: " + UTF16_SUBJECT, null);
|
||||
checkFields(UTF16_REPLYTO + ", ", null, null, "Re: " + UTF16_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -245,7 +248,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF32_SENDER + ", ", null, null, "Re: " + UTF32_SUBJECT, null);
|
||||
checkFields(UTF32_SENDER + ", ", null, null, "Re: " + UTF32_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -257,7 +260,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF32_REPLYTO + ", ", null, null, "Re: " + UTF32_SUBJECT, null);
|
||||
checkFields(UTF32_REPLYTO + ", ", null, null, "Re: " + UTF32_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -278,7 +281,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(null, null, null, "Fwd: " + SUBJECT, null);
|
||||
checkFields(null, null, null, "Fwd: " + SUBJECT, null, null);
|
||||
checkFocused(mToView);
|
||||
}
|
||||
});
|
||||
|
@ -303,7 +306,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, SUBJECT, BODY);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, SUBJECT, BODY, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -316,7 +319,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, null, BODY);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, null, BODY, null);
|
||||
checkFocused(mSubjectView);
|
||||
}
|
||||
});
|
||||
|
@ -339,7 +342,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF16_RECIPIENT_TO + ", ",
|
||||
null, null, UTF16_SUBJECT, UTF16_BODY);
|
||||
null, null, UTF16_SUBJECT, UTF16_BODY, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -352,7 +355,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF16_RECIPIENT_TO + ", ", null, null, null, UTF16_BODY);
|
||||
checkFields(UTF16_RECIPIENT_TO + ", ", null, null, null, UTF16_BODY, null);
|
||||
checkFocused(mSubjectView);
|
||||
}
|
||||
});
|
||||
|
@ -375,7 +378,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF32_RECIPIENT_TO + ", ",
|
||||
null, null, UTF32_SUBJECT, UTF32_BODY);
|
||||
null, null, UTF32_SUBJECT, UTF32_BODY, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -388,7 +391,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
resetViews();
|
||||
a.processSourceMessage(message, null);
|
||||
checkFields(UTF32_RECIPIENT_TO + ", ", null, null, null, UTF32_BODY);
|
||||
checkFields(UTF32_RECIPIENT_TO + ", ", null, null, null, UTF32_BODY, null);
|
||||
checkFocused(mSubjectView);
|
||||
}
|
||||
});
|
||||
|
@ -555,7 +558,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.initFromIntent(i2);
|
||||
checkFields(RECIPIENT_TO + ", ", RECIPIENT_CC, RECIPIENT_BCC, SUBJECT, null);
|
||||
checkFields(RECIPIENT_TO + ", ", RECIPIENT_CC, RECIPIENT_BCC, SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -579,7 +582,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
a.initFromIntent(i2);
|
||||
checkFields(UTF16_RECIPIENT_TO + ", ",
|
||||
UTF16_RECIPIENT_CC, UTF16_RECIPIENT_BCC, UTF16_SUBJECT, null);
|
||||
UTF16_RECIPIENT_CC, UTF16_RECIPIENT_BCC, UTF16_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -603,7 +606,7 @@ public class MessageComposeInstrumentationTests
|
|||
public void run() {
|
||||
a.initFromIntent(i2);
|
||||
checkFields(UTF32_RECIPIENT_TO + ", ",
|
||||
UTF32_RECIPIENT_CC, UTF32_RECIPIENT_BCC, UTF32_SUBJECT, null);
|
||||
UTF32_RECIPIENT_CC, UTF32_RECIPIENT_BCC, UTF32_SUBJECT, null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -625,7 +628,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.initFromIntent(i2);
|
||||
checkFields(null, null, null, null, BODY);
|
||||
checkFields(null, null, null, null, BODY, null);
|
||||
checkFocused(mToView);
|
||||
}
|
||||
});
|
||||
|
@ -649,7 +652,7 @@ public class MessageComposeInstrumentationTests
|
|||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
a.initFromIntent(i2);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, "This is the subject", null);
|
||||
checkFields(RECIPIENT_TO + ", ", null, null, "This is the subject", null, null);
|
||||
checkFocused(mMessageView);
|
||||
}
|
||||
});
|
||||
|
@ -670,8 +673,10 @@ public class MessageComposeInstrumentationTests
|
|||
* @param bcc expected value (null = it must be empty)
|
||||
* @param subject expected value (null = it must be empty)
|
||||
* @param content expected value (null = it must be empty)
|
||||
* @param signature expected value (null = it must be empty)
|
||||
*/
|
||||
private void checkFields(String to, String cc, String bcc, String subject, String content) {
|
||||
private void checkFields(String to, String cc, String bcc, String subject, String content,
|
||||
String signature) {
|
||||
String toText = mToView.getText().toString();
|
||||
if (to == null) {
|
||||
assertEquals(0, toText.length());
|
||||
|
@ -690,6 +695,13 @@ public class MessageComposeInstrumentationTests
|
|||
if (content == null) {
|
||||
assertEquals(0, contentText.length());
|
||||
} else {
|
||||
if (signature != null) {
|
||||
int textLength = content.length();
|
||||
if (textLength == 0 || content.charAt(textLength - 1) != '\n') {
|
||||
content += "\n";
|
||||
}
|
||||
content += signature;
|
||||
}
|
||||
assertEquals(content, contentText);
|
||||
}
|
||||
}
|
||||
|
@ -781,6 +793,49 @@ public class MessageComposeInstrumentationTests
|
|||
assertEquals("a@b.c, foo, ", mToView.getText().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check message and selection with/without signature.
|
||||
*/
|
||||
public void testSetInitialComposeTextAndSelection() throws MessagingException, Throwable {
|
||||
final Message msg = buildTestMessage(null, null, null, BODY);
|
||||
final Intent intent = new Intent(ACTION_EDIT_DRAFT);
|
||||
final Account account = new Account();
|
||||
final MessageCompose a = getActivity();
|
||||
a.setIntent(intent);
|
||||
|
||||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
resetViews();
|
||||
a.setInitialComposeText(BODY, SIGNATURE);
|
||||
checkFields(null, null, null, null, BODY, SIGNATURE);
|
||||
a.setMessageContentSelection(SIGNATURE);
|
||||
assertEquals(BODY.length(), mMessageView.getSelectionStart());
|
||||
}
|
||||
});
|
||||
|
||||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
resetViews();
|
||||
a.setInitialComposeText(BODY, null);
|
||||
checkFields(null, null, null, null, BODY, null);
|
||||
a.setMessageContentSelection(null);
|
||||
assertEquals(BODY.length(), mMessageView.getSelectionStart());
|
||||
}
|
||||
});
|
||||
|
||||
runTestOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
resetViews();
|
||||
final String body2 = BODY + "\n\na\n\n";
|
||||
a.setInitialComposeText(body2, SIGNATURE);
|
||||
checkFields(null, null, null, null, body2, SIGNATURE);
|
||||
a.setMessageContentSelection(SIGNATURE);
|
||||
assertEquals(BODY.length() + 3, mMessageView.getSelectionStart());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the comma-inserting logic. The logic is applied equally to To: Cc: and Bcc:
|
||||
* but we only run the full set on To:
|
||||
|
|
Loading…
Reference in New Issue