1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-12-02 14:02:17 -05:00

Put the logic that decides which message format to use in one place

This change is in preparation for the future rich text editing
functionality.
This commit is contained in:
cketti 2012-05-20 22:44:32 +02:00
parent 71b3b2916e
commit e88633cf78

View File

@ -1,19 +1,6 @@
package com.fsck.k9.activity; package com.fsck.k9.activity;
import java.io.File;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.text.*;
import android.webkit.WebViewClient;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.StringUtils;
import com.fsck.k9.mail.*;
import com.fsck.k9.view.MessageWebView;
import org.apache.james.mime4j.codec.EncoderUtil;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.app.Dialog; import android.app.Dialog;
@ -30,6 +17,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.text.TextWatcher;
import android.text.util.Rfc822Tokenizer; import android.text.util.Rfc822Tokenizer;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
@ -42,25 +30,22 @@ import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AutoCompleteTextView.Validator; import android.widget.AutoCompleteTextView.Validator;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.MultiAutoCompleteTextView; import android.widget.MultiAutoCompleteTextView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleHtmlSerializer;
import org.htmlcleaner.TagNode;
import com.fsck.k9.Account; import com.fsck.k9.Account;
import com.fsck.k9.Account.QuoteStyle;
import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.Account.MessageFormat;
import com.fsck.k9.Account.QuoteStyle;
import com.fsck.k9.EmailAddressAdapter; import com.fsck.k9.EmailAddressAdapter;
import com.fsck.k9.EmailAddressValidator; import com.fsck.k9.EmailAddressValidator;
import com.fsck.k9.FontSizes; import com.fsck.k9.FontSizes;
@ -74,8 +59,17 @@ import com.fsck.k9.crypto.CryptoProvider;
import com.fsck.k9.crypto.PgpData; import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.helper.ContactItem; import com.fsck.k9.helper.ContactItem;
import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.Contacts;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.StringUtils;
import com.fsck.k9.helper.Utility; import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
@ -84,6 +78,24 @@ import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody; import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
import com.fsck.k9.view.MessageWebView;
import org.apache.james.mime4j.codec.EncoderUtil;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleHtmlSerializer;
import org.htmlcleaner.TagNode;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener { public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener {
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1; private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
@ -123,9 +135,12 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private static final String STATE_PGP_DATA = "pgpData"; private static final String STATE_PGP_DATA = "pgpData";
private static final String STATE_IN_REPLY_TO = "com.fsck.k9.activity.MessageCompose.inReplyTo"; private static final String STATE_IN_REPLY_TO = "com.fsck.k9.activity.MessageCompose.inReplyTo";
private static final String STATE_REFERENCES = "com.fsck.k9.activity.MessageCompose.references"; private static final String STATE_REFERENCES = "com.fsck.k9.activity.MessageCompose.references";
private static final String STATE_KEY_MESSAGE_FORMAT = "com.fsck.k9.activity.MessageCompose.messageFormat";
private static final String STATE_KEY_READ_RECEIPT = "com.fsck.k9.activity.MessageCompose.messageReadReceipt"; private static final String STATE_KEY_READ_RECEIPT = "com.fsck.k9.activity.MessageCompose.messageReadReceipt";
private static final String STATE_KEY_DRAFT_NEEDS_SAVING = "com.fsck.k9.activity.MessageCompose.mDraftNeedsSaving"; private static final String STATE_KEY_DRAFT_NEEDS_SAVING = "com.fsck.k9.activity.MessageCompose.mDraftNeedsSaving";
private static final String STATE_KEY_FORCE_PLAIN_TEXT =
"com.fsck.k9.activity.MessageCompose.forcePlainText";
private static final String STATE_KEY_QUOTED_TEXT_FORMAT =
"com.fsck.k9.activity.MessageCompose.quotedTextFormat";
private static final int MSG_PROGRESS_ON = 1; private static final int MSG_PROGRESS_ON = 1;
private static final int MSG_PROGRESS_OFF = 2; private static final int MSG_PROGRESS_OFF = 2;
@ -176,6 +191,21 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private MessageReference mMessageReference; private MessageReference mMessageReference;
private Message mSourceMessage; private Message mSourceMessage;
/**
* "Original" message body
*
* <p>
* The contents of this string will be used instead of the body of a referenced message when
* replying to or forwarding a message.<br>
* Right now this is only used when replying to a signed or encrypted message. It then contains
* the stripped/decrypted body of that message.
* </p>
* <p><strong>Note:</strong>
* When this field is not {@code null} we assume that the message we are composing right now
* should be encrypted.
* </p>
*/
private String mSourceMessageBody; private String mSourceMessageBody;
/** /**
@ -208,6 +238,17 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private QuotedTextMode mQuotedTextMode = QuotedTextMode.NONE; private QuotedTextMode mQuotedTextMode = QuotedTextMode.NONE;
/**
* Contains the format of the quoted text (text vs. HTML).
*/
private SimpleMessageFormat mQuotedTextFormat;
/**
* When this it {@code true} the message format setting is ignored and we're always sending
* a text/plain message.
*/
private boolean mForcePlainText = false;
private Button mChooseIdentityButton; private Button mChooseIdentityButton;
private LinearLayout mCcWrapper; private LinearLayout mCcWrapper;
private LinearLayout mBccWrapper; private LinearLayout mBccWrapper;
@ -243,7 +284,22 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private String mInReplyTo; private String mInReplyTo;
private boolean mSourceProcessed = false; private boolean mSourceProcessed = false;
private MessageFormat mMessageFormat;
enum SimpleMessageFormat {
TEXT,
HTML
}
/**
* The currently used message format.
*
* <p>
* <strong>Note:</strong>
* Don't modify this field directly. Use {@link #updateMessageFormat()}.
* </p>
*/
private SimpleMessageFormat mMessageFormat;
private QuoteStyle mQuoteStyle; private QuoteStyle mQuoteStyle;
private boolean mDraftNeedsSaving = false; private boolean mDraftNeedsSaving = false;
@ -638,17 +694,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mSignatureView.setVisibility(View.GONE); mSignatureView.setVisibility(View.GONE);
} }
mMessageFormat = mAccount.getMessageFormat();
if (mMessageFormat == MessageFormat.AUTO) {
if (mAction == Action.COMPOSE) {
mMessageFormat = MessageFormat.TEXT;
} else if (mSourceMessageBody != null) {
// mSourceMessageBody is set to something when replying to and forwarding decrypted
// messages, so we set the format to plain text.
mMessageFormat = MessageFormat.TEXT;
}
}
mReadReceipt = mAccount.isMessageReadReceiptAlways(); mReadReceipt = mAccount.isMessageReadReceiptAlways();
mQuoteStyle = mAccount.getQuoteStyle(); mQuoteStyle = mAccount.getQuoteStyle();
@ -710,6 +755,13 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mCryptoSignatureUserId = (TextView)findViewById(R.id.userId); mCryptoSignatureUserId = (TextView)findViewById(R.id.userId);
mCryptoSignatureUserIdRest = (TextView)findViewById(R.id.userIdRest); mCryptoSignatureUserIdRest = (TextView)findViewById(R.id.userIdRest);
mEncryptCheckbox = (CheckBox)findViewById(R.id.cb_encrypt); mEncryptCheckbox = (CheckBox)findViewById(R.id.cb_encrypt);
mEncryptCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateMessageFormat();
}
});
if (mSourceMessageBody != null) { if (mSourceMessageBody != null) {
// mSourceMessageBody is set to something when replying to and forwarding decrypted // mSourceMessageBody is set to something when replying to and forwarding decrypted
// messages, so the sender probably wants the message to be encrypted. // messages, so the sender probably wants the message to be encrypted.
@ -764,6 +816,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mMessageContentView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize); mMessageContentView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
mQuotedText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize); mQuotedText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
mSignatureView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize); mSignatureView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
updateMessageFormat();
} }
/** /**
@ -922,7 +976,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mCryptoSignatureUserId.setVisibility(View.INVISIBLE); mCryptoSignatureUserId.setVisibility(View.INVISIBLE);
mCryptoSignatureUserIdRest.setVisibility(View.INVISIBLE); mCryptoSignatureUserIdRest.setVisibility(View.INVISIBLE);
} else { } else {
mMessageFormat = MessageFormat.TEXT;
// if a signature key is selected, then the checkbox itself has no text // if a signature key is selected, then the checkbox itself has no text
mCryptoSignatureCheckbox.setText(""); mCryptoSignatureCheckbox.setText("");
mCryptoSignatureCheckbox.setChecked(true); mCryptoSignatureCheckbox.setChecked(true);
@ -945,6 +998,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
} }
} }
} }
updateMessageFormat();
} }
@Override @Override
@ -994,9 +1049,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
outState.putString(STATE_IN_REPLY_TO, mInReplyTo); outState.putString(STATE_IN_REPLY_TO, mInReplyTo);
outState.putString(STATE_REFERENCES, mReferences); outState.putString(STATE_REFERENCES, mReferences);
outState.putSerializable(STATE_KEY_HTML_QUOTE, mQuotedHtmlContent); outState.putSerializable(STATE_KEY_HTML_QUOTE, mQuotedHtmlContent);
outState.putSerializable(STATE_KEY_MESSAGE_FORMAT, mMessageFormat);
outState.putBoolean(STATE_KEY_READ_RECEIPT, mReadReceipt); outState.putBoolean(STATE_KEY_READ_RECEIPT, mReadReceipt);
outState.putBoolean(STATE_KEY_DRAFT_NEEDS_SAVING, mDraftNeedsSaving); outState.putBoolean(STATE_KEY_DRAFT_NEEDS_SAVING, mDraftNeedsSaving);
outState.putBoolean(STATE_KEY_FORCE_PLAIN_TEXT, mForcePlainText);
outState.putSerializable(STATE_KEY_QUOTED_TEXT_FORMAT, mQuotedTextFormat);
} }
@Override @Override
@ -1009,21 +1065,22 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
addAttachment(uri); addAttachment(uri);
} }
mMessageFormat = (MessageFormat) savedInstanceState
.getSerializable(STATE_KEY_MESSAGE_FORMAT);
mReadReceipt = savedInstanceState mReadReceipt = savedInstanceState
.getBoolean(STATE_KEY_READ_RECEIPT); .getBoolean(STATE_KEY_READ_RECEIPT);
mCcWrapper.setVisibility(savedInstanceState.getBoolean(STATE_KEY_CC_SHOWN) ? View.VISIBLE mCcWrapper.setVisibility(savedInstanceState.getBoolean(STATE_KEY_CC_SHOWN) ? View.VISIBLE
: View.GONE); : View.GONE);
mBccWrapper.setVisibility(savedInstanceState mBccWrapper.setVisibility(savedInstanceState
.getBoolean(STATE_KEY_BCC_SHOWN) ? View.VISIBLE : View.GONE); .getBoolean(STATE_KEY_BCC_SHOWN) ? View.VISIBLE : View.GONE);
showOrHideQuotedText((QuotedTextMode)savedInstanceState.getSerializable(STATE_KEY_QUOTED_TEXT_MODE));
if (mQuotedTextMode != QuotedTextMode.NONE && mMessageFormat == MessageFormat.HTML) { showOrHideQuotedText(
mQuotedHtmlContent = (InsertableHtmlContent) savedInstanceState.getSerializable(STATE_KEY_HTML_QUOTE); (QuotedTextMode) savedInstanceState.getSerializable(STATE_KEY_QUOTED_TEXT_MODE));
mQuotedHtmlContent =
(InsertableHtmlContent) savedInstanceState.getSerializable(STATE_KEY_HTML_QUOTE);
if (mQuotedHtmlContent != null && mQuotedHtmlContent.getQuotedContent() != null) { if (mQuotedHtmlContent != null && mQuotedHtmlContent.getQuotedContent() != null) {
mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html"); mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html");
} }
}
mDraftId = savedInstanceState.getLong(STATE_KEY_DRAFT_ID); mDraftId = savedInstanceState.getLong(STATE_KEY_DRAFT_ID);
mIdentity = (Identity)savedInstanceState.getSerializable(STATE_IDENTITY); mIdentity = (Identity)savedInstanceState.getSerializable(STATE_IDENTITY);
mIdentityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED); mIdentityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED);
@ -1031,12 +1088,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mInReplyTo = savedInstanceState.getString(STATE_IN_REPLY_TO); mInReplyTo = savedInstanceState.getString(STATE_IN_REPLY_TO);
mReferences = savedInstanceState.getString(STATE_REFERENCES); mReferences = savedInstanceState.getString(STATE_REFERENCES);
mDraftNeedsSaving = savedInstanceState.getBoolean(STATE_KEY_DRAFT_NEEDS_SAVING); mDraftNeedsSaving = savedInstanceState.getBoolean(STATE_KEY_DRAFT_NEEDS_SAVING);
mForcePlainText = savedInstanceState.getBoolean(STATE_KEY_FORCE_PLAIN_TEXT);
mQuotedTextFormat = (SimpleMessageFormat) savedInstanceState.getSerializable(
STATE_KEY_QUOTED_TEXT_FORMAT);
initializeCrypto(); initializeCrypto();
updateFrom(); updateFrom();
updateSignature(); updateSignature();
updateEncryptLayout(); updateEncryptLayout();
updateMessageFormat();
} }
private void updateTitle() { private void updateTitle() {
@ -1109,7 +1170,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
* @return {@link TextBody} instance that contains the entered text and possibly the quoted * @return {@link TextBody} instance that contains the entered text and possibly the quoted
* original message. * original message.
*/ */
private TextBody buildText(boolean isDraft, MessageFormat messageFormat) { private TextBody buildText(boolean isDraft, SimpleMessageFormat messageFormat) {
// The length of the formatted version of the user-supplied text/reply // The length of the formatted version of the user-supplied text/reply
int composedMessageLength; int composedMessageLength;
@ -1135,7 +1196,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
String text = mMessageContentView.getText().toString(); String text = mMessageContentView.getText().toString();
// Handle HTML separate from the rest of the text content // Handle HTML separate from the rest of the text content
if (messageFormat == MessageFormat.HTML) { if (messageFormat == SimpleMessageFormat.HTML) {
// Do we have to modify an existing message to include our reply? // Do we have to modify an existing message to include our reply?
if (includeQuotedText && mQuotedHtmlContent != null) { if (includeQuotedText && mQuotedHtmlContent != null) {
@ -1293,14 +1354,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
final boolean hasAttachments = mAttachments.getChildCount() > 0; final boolean hasAttachments = mAttachments.getChildCount() > 0;
if (mMessageFormat == MessageFormat.HTML) { if (mMessageFormat == SimpleMessageFormat.HTML) {
// HTML message (with alternative text part) // HTML message (with alternative text part)
// This is the compiled MIME part for an HTML message. // This is the compiled MIME part for an HTML message.
MimeMultipart composedMimeMessage = new MimeMultipart(); MimeMultipart composedMimeMessage = new MimeMultipart();
composedMimeMessage.setSubType("alternative"); // Let the receiver select either the text or the HTML part. composedMimeMessage.setSubType("alternative"); // Let the receiver select either the text or the HTML part.
composedMimeMessage.addBodyPart(new MimeBodyPart(body, "text/html")); composedMimeMessage.addBodyPart(new MimeBodyPart(body, "text/html"));
bodyPlain = buildText(isDraft, MessageFormat.TEXT); bodyPlain = buildText(isDraft, SimpleMessageFormat.TEXT);
composedMimeMessage.addBodyPart(new MimeBodyPart(bodyPlain, "text/plain")); composedMimeMessage.addBodyPart(new MimeBodyPart(bodyPlain, "text/plain"));
if (hasAttachments) { if (hasAttachments) {
@ -1316,7 +1377,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
// If no attachments, our multipart/alternative part is the only one we need. // If no attachments, our multipart/alternative part is the only one we need.
message.setBody(composedMimeMessage); message.setBody(composedMimeMessage);
} }
} else { } else if (mMessageFormat == SimpleMessageFormat.TEXT) {
// Text-only message. // Text-only message.
if (hasAttachments) { if (hasAttachments) {
MimeMultipart mp = new MimeMultipart(); MimeMultipart mp = new MimeMultipart();
@ -1647,7 +1708,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
} }
final CryptoProvider crypto = mAccount.getCryptoProvider(); final CryptoProvider crypto = mAccount.getCryptoProvider();
if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) { if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) {
mMessageFormat = MessageFormat.TEXT;
// key selection before encryption // key selection before encryption
StringBuilder emails = new StringBuilder(); StringBuilder emails = new StringBuilder();
for (Address address : getRecipientAddresses()) { for (Address address : getRecipientAddresses()) {
@ -1969,6 +2029,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mDraftNeedsSaving = true; mDraftNeedsSaving = true;
updateFrom(); updateFrom();
updateSignature(); updateSignature();
updateMessageFormat();
} }
private void updateFrom() { private void updateFrom() {
@ -1998,14 +2059,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
break; break;
case R.id.quoted_text_show: case R.id.quoted_text_show:
showOrHideQuotedText(QuotedTextMode.SHOW); showOrHideQuotedText(QuotedTextMode.SHOW);
updateMessageFormat();
mDraftNeedsSaving = true; mDraftNeedsSaving = true;
break; break;
case R.id.quoted_text_delete: case R.id.quoted_text_delete:
showOrHideQuotedText(QuotedTextMode.HIDE); showOrHideQuotedText(QuotedTextMode.HIDE);
updateMessageFormat();
mDraftNeedsSaving = true; mDraftNeedsSaving = true;
break; break;
case R.id.quoted_text_edit: case R.id.quoted_text_edit:
mMessageFormat = MessageFormat.TEXT; mForcePlainText = true;
if (mMessageReference != null) { // shouldn't happen... if (mMessageReference != null) { // shouldn't happen...
// TODO - Should we check if mSourceMessageBody is already present and bypass the MessagingController call? // TODO - Should we check if mSourceMessageBody is already present and bypass the MessagingController call?
MessagingController.getInstance(getApplication()).addListener(mListener); MessagingController.getInstance(getApplication()).addListener(mListener);
@ -2021,22 +2084,33 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
} }
} }
/* /**
* Show or Hide the quoted text according to mQuotedTextMode. * Show or hide the quoted text.
*
* @param mode
* The value to set {@link #mQuotedTextMode} to.
*/ */
private void showOrHideQuotedText(QuotedTextMode mode) { private void showOrHideQuotedText(QuotedTextMode mode) {
mQuotedTextMode = mode; mQuotedTextMode = mode;
if (mQuotedTextMode == QuotedTextMode.NONE) { switch (mode) {
case NONE:
case HIDE: {
if (mode == QuotedTextMode.NONE) {
mQuotedTextShow.setVisibility(View.GONE); mQuotedTextShow.setVisibility(View.GONE);
} else {
mQuotedTextShow.setVisibility(View.VISIBLE);
}
mQuotedTextBar.setVisibility(View.GONE); mQuotedTextBar.setVisibility(View.GONE);
mQuotedText.setVisibility(View.GONE); mQuotedText.setVisibility(View.GONE);
mQuotedHTML.setVisibility(View.GONE); mQuotedHTML.setVisibility(View.GONE);
mQuotedTextEdit.setVisibility(View.GONE); mQuotedTextEdit.setVisibility(View.GONE);
} else if (mQuotedTextMode == QuotedTextMode.SHOW) { break;
}
case SHOW: {
mQuotedTextShow.setVisibility(View.GONE); mQuotedTextShow.setVisibility(View.GONE);
mQuotedTextBar.setVisibility(View.VISIBLE); mQuotedTextBar.setVisibility(View.VISIBLE);
if (mMessageFormat == MessageFormat.HTML) {
if (mQuotedTextFormat == SimpleMessageFormat.HTML) {
mQuotedText.setVisibility(View.GONE); mQuotedText.setVisibility(View.GONE);
mQuotedHTML.setVisibility(View.VISIBLE); mQuotedHTML.setVisibility(View.VISIBLE);
mQuotedTextEdit.setVisibility(View.VISIBLE); mQuotedTextEdit.setVisibility(View.VISIBLE);
@ -2045,13 +2119,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mQuotedHTML.setVisibility(View.GONE); mQuotedHTML.setVisibility(View.GONE);
mQuotedTextEdit.setVisibility(View.GONE); mQuotedTextEdit.setVisibility(View.GONE);
} }
} else if (mQuotedTextMode == QuotedTextMode.HIDE) { break;
mQuotedTextShow.setVisibility(View.VISIBLE); }
mQuotedTextBar.setVisibility(View.GONE);
mQuotedText.setVisibility(View.GONE);
mQuotedHTML.setVisibility(View.GONE);
mQuotedTextEdit.setVisibility(View.GONE);
} }
} }
@ -2319,6 +2388,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mSourceMessageProcessed = true; mSourceMessageProcessed = true;
mDraftNeedsSaving = false; mDraftNeedsSaving = false;
} }
updateMessageFormat();
} }
private void processMessageToReplyTo(Message message) throws MessagingException { private void processMessageToReplyTo(Message message) throws MessagingException {
@ -2573,27 +2644,44 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
? QuoteStyle.valueOf(k9identity.get(IdentityField.QUOTE_STYLE)) ? QuoteStyle.valueOf(k9identity.get(IdentityField.QUOTE_STYLE))
: mAccount.getQuoteStyle(); : mAccount.getQuoteStyle();
QuotedTextMode quotedMode;
try {
quotedMode = QuotedTextMode.valueOf(showQuotedTextMode);
} catch (Exception e) {
quotedMode = QuotedTextMode.NONE;
}
// Always respect the user's current composition format preference, even if the // Always respect the user's current composition format preference, even if the
// draft was saved in a different format. // draft was saved in a different format.
// TODO - The current implementation doesn't allow a user in HTML mode to edit a draft that wasn't saved with K9mail. // TODO - The current implementation doesn't allow a user in HTML mode to edit a draft that wasn't saved with K9mail.
String messageFormat = k9identity.get(IdentityField.MESSAGE_FORMAT); String messageFormatString = k9identity.get(IdentityField.MESSAGE_FORMAT);
MessageFormat messageFormat = null;
if (messageFormatString != null) {
try {
messageFormat = MessageFormat.valueOf(messageFormatString);
} catch (Exception e) { /* do nothing */ }
}
if (messageFormat == null) { if (messageFormat == null) {
// This message probably wasn't created by us. The exception is legacy // This message probably wasn't created by us. The exception is legacy
// drafts created before the advent of HTML composition. In those cases, // drafts created before the advent of HTML composition. In those cases,
// we'll display the whole message (including the quoted part) in the // we'll display the whole message (including the quoted part) in the
// composition window. If that's the case, try and convert it to text to // composition window. If that's the case, try and convert it to text to
// match the behavior in text mode. // match the behavior in text mode.
mMessageContentView.setText(getBodyTextFromMessage(message, MessageFormat.TEXT)); mMessageContentView.setText(getBodyTextFromMessage(message, SimpleMessageFormat.TEXT));
mMessageFormat = MessageFormat.TEXT; mForcePlainText = true;
showOrHideQuotedText(QuotedTextMode.valueOf(showQuotedTextMode));
showOrHideQuotedText(quotedMode);
return; return;
} }
mMessageFormat = MessageFormat.valueOf(messageFormat);
if (mMessageFormat == MessageFormat.HTML) { if (messageFormat == MessageFormat.HTML) {
Part part = MimeUtility.findFirstPartByMimeType(message, "text/html"); Part part = MimeUtility.findFirstPartByMimeType(message, "text/html");
if (part != null) { // Shouldn't happen if we were the one who saved it. if (part != null) { // Shouldn't happen if we were the one who saved it.
mQuotedTextFormat = SimpleMessageFormat.HTML;
String text = MimeUtility.getTextFromPart(part); String text = MimeUtility.getTextFromPart(part);
if (K9.DEBUG) { if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
@ -2623,7 +2711,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
if (bodyPlainOffset != null && bodyPlainLength != null) { if (bodyPlainOffset != null && bodyPlainLength != null) {
processSourceMessageText(message, bodyPlainOffset, bodyPlainLength, false); processSourceMessageText(message, bodyPlainOffset, bodyPlainLength, false);
} }
} else if (mMessageFormat == MessageFormat.TEXT) { } else if (messageFormat == MessageFormat.TEXT) {
mQuotedTextFormat = SimpleMessageFormat.TEXT;
processSourceMessageText(message, bodyOffset, bodyLength, true); processSourceMessageText(message, bodyOffset, bodyLength, true);
} else { } else {
Log.e(K9.LOG_TAG, "Unhandled message format."); Log.e(K9.LOG_TAG, "Unhandled message format.");
@ -2636,7 +2725,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
Log.e(K9.LOG_TAG, "Could not set cursor position in MessageCompose; ignoring.", e); Log.e(K9.LOG_TAG, "Could not set cursor position in MessageCompose; ignoring.", e);
} }
showOrHideQuotedText(QuotedTextMode.valueOf(showQuotedTextMode)); showOrHideQuotedText(quotedMode);
} }
/* /*
@ -2699,20 +2788,29 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
* @throws MessagingException * @throws MessagingException
*/ */
private void populateUIWithQuotedMessage(boolean showQuotedText) throws MessagingException { private void populateUIWithQuotedMessage(boolean showQuotedText) throws MessagingException {
if (mMessageFormat == MessageFormat.AUTO) { MessageFormat origMessageFormat = mAccount.getMessageFormat();
mMessageFormat = MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/html") == null if (mForcePlainText || origMessageFormat == MessageFormat.TEXT) {
? MessageFormat.TEXT // Use plain text for the quoted message
: MessageFormat.HTML; mQuotedTextFormat = SimpleMessageFormat.TEXT;
} else if (origMessageFormat == MessageFormat.AUTO) {
// Figure out which message format to use for the quoted text by looking if the source
// message contains a text/html part. If it does, we use that.
mQuotedTextFormat =
(MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/html") == null) ?
SimpleMessageFormat.TEXT : SimpleMessageFormat.HTML;
} else {
mQuotedTextFormat = SimpleMessageFormat.HTML;
} }
// TODO -- I am assuming that mSourceMessageBody will always be a text part. Is this a safe assumption? // TODO -- I am assuming that mSourceMessageBody will always be a text part. Is this a safe assumption?
// Handle the original message in the reply // Handle the original message in the reply
// If we already have mSourceMessageBody, use that. It's pre-populated if we've got crypto going on. // If we already have mSourceMessageBody, use that. It's pre-populated if we've got crypto going on.
String content = mSourceMessageBody != null String content = (mSourceMessageBody != null) ?
? mSourceMessageBody mSourceMessageBody :
: getBodyTextFromMessage(mSourceMessage, mMessageFormat); getBodyTextFromMessage(mSourceMessage, mQuotedTextFormat);
if (mMessageFormat == MessageFormat.HTML) {
if (mQuotedTextFormat == SimpleMessageFormat.HTML) {
// Strip signature. // Strip signature.
// closing tags such as </div>, </span>, </table>, </pre> will be cut off. // closing tags such as </div>, </span>, </table>, </pre> will be cut off.
if (mAccount.isStripSignature() && if (mAccount.isStripSignature() &&
@ -2786,20 +2884,25 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
Log.e(K9.LOG_TAG, "Problem cleaning quoted message.", ioe); Log.e(K9.LOG_TAG, "Problem cleaning quoted message.", ioe);
} }
} }
// Add the HTML reply header to the top of the content. // Add the HTML reply header to the top of the content.
mQuotedHtmlContent = quoteOriginalHtmlMessage(mSourceMessage, content, mQuoteStyle); mQuotedHtmlContent = quoteOriginalHtmlMessage(mSourceMessage, content, mQuoteStyle);
// Load the message with the reply header. // Load the message with the reply header.
mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html"); mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html");
mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage,
getBodyTextFromMessage(mSourceMessage, MessageFormat.TEXT), mQuoteStyle));
} else if (mMessageFormat == MessageFormat.TEXT) { // TODO: Also strip the signature from the text/plain part
mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage,
getBodyTextFromMessage(mSourceMessage, SimpleMessageFormat.TEXT), mQuoteStyle));
} else if (mQuotedTextFormat == SimpleMessageFormat.TEXT) {
if (mAccount.isStripSignature() && if (mAccount.isStripSignature() &&
(mAction == Action.REPLY || mAction == Action.REPLY_ALL)) { (mAction == Action.REPLY || mAction == Action.REPLY_ALL)) {
if (DASH_SIGNATURE_PLAIN.matcher(content).find()) { if (DASH_SIGNATURE_PLAIN.matcher(content).find()) {
content = DASH_SIGNATURE_PLAIN.matcher(content).replaceFirst("\r\n"); content = DASH_SIGNATURE_PLAIN.matcher(content).replaceFirst("\r\n");
} }
} }
mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage, content, mQuoteStyle)); mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage, content, mQuoteStyle));
} }
@ -2818,9 +2921,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
* @return Text in desired format. * @return Text in desired format.
* @throws MessagingException * @throws MessagingException
*/ */
private String getBodyTextFromMessage(final Message message, final MessageFormat format) throws MessagingException { private String getBodyTextFromMessage(final Message message, final SimpleMessageFormat format)
throws MessagingException {
Part part; Part part;
if (format == MessageFormat.HTML) { if (format == SimpleMessageFormat.HTML) {
// HTML takes precedence, then text. // HTML takes precedence, then text.
part = MimeUtility.findFirstPartByMimeType(message, "text/html"); part = MimeUtility.findFirstPartByMimeType(message, "text/html");
if (part != null) { if (part != null) {
@ -2835,7 +2939,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found."); Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part), true); return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part), true);
} }
} else if (format == MessageFormat.TEXT) { } else if (format == SimpleMessageFormat.TEXT) {
// Text takes precedence, then html. // Text takes precedence, then html.
part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
if (part != null) { if (part != null) {
@ -3025,6 +3129,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
showOrHideQuotedText(QuotedTextMode.HIDE); showOrHideQuotedText(QuotedTextMode.HIDE);
Log.e(K9.LOG_TAG, "Could not re-process source message; deleting quoted text to be safe.", e); Log.e(K9.LOG_TAG, "Could not re-process source message; deleting quoted text to be safe.", e);
} }
updateMessageFormat();
} else { } else {
processSourceMessage(message); processSourceMessage(message);
mSourceProcessed = true; mSourceProcessed = true;
@ -3436,4 +3541,47 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private static String getIdentityDescription(Identity identity) { private static String getIdentityDescription(Identity identity) {
return String.format("%s <%s>", identity.getName(), identity.getEmail()); return String.format("%s <%s>", identity.getName(), identity.getEmail());
} }
private void setMessageFormat(SimpleMessageFormat format) {
// This method will later be used to enable/disable the rich text editing mode.
mMessageFormat = format;
}
private void updateMessageFormat() {
MessageFormat origMessageFormat = mAccount.getMessageFormat();
SimpleMessageFormat messageFormat;
if (origMessageFormat == MessageFormat.TEXT) {
// The user wants to send text/plain messages. We don't override that choice under
// any circumstances.
messageFormat = SimpleMessageFormat.TEXT;
} else if (mForcePlainText && includeQuotedText()) {
// Right now we send a text/plain-only message when the quoted text was edited, no
// matter what the user selected for the message format.
messageFormat = SimpleMessageFormat.TEXT;
} else if (mEncryptCheckbox.isChecked() || mCryptoSignatureCheckbox.isChecked()) {
// Right now we only support PGP inline which doesn't play well with HTML. So force
// plain text in those cases.
messageFormat = SimpleMessageFormat.TEXT;
} else if (origMessageFormat == MessageFormat.AUTO) {
if (mAction == Action.COMPOSE || mQuotedTextFormat == SimpleMessageFormat.TEXT ||
!includeQuotedText()) {
// If the message format is set to "AUTO" we use text/plain whenever possible. That
// is, when composing new messages and replying to or forwarding text/plain
// messages.
messageFormat = SimpleMessageFormat.TEXT;
} else {
messageFormat = SimpleMessageFormat.HTML;
}
} else {
// In all other cases use HTML
messageFormat = SimpleMessageFormat.HTML;
}
setMessageFormat(messageFormat);
}
private boolean includeQuotedText() {
return (mQuotedTextMode == QuotedTextMode.SHOW);
}
} }