diff --git a/res/layout/message_compose.xml b/res/layout/message_compose.xml index 497b6ffbf..d0ef2f14a 100644 --- a/res/layout/message_compose.xml +++ b/res/layout/message_compose.xml @@ -216,7 +216,8 @@ - - - - De: A: A/c: - %s ha escrit:\n + %s ha escrit: Has d\'afegir-hi, com a mínim, un destinatari. No s\'ha trobat cap adreça de correu. Alguns adjunts no es poden reenviar perquè no s\'han carregat. diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 9b99454c4..f36f7ad82 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -267,7 +267,7 @@ Posílejte prosím chybová hlášení, přispívejte novými funkcemi a ptejte Odesílatel: Komu: Kopie: - %s napsal(a):\n + %s napsal(a): Musíte přidat alespoň jednoho příjemce. Nemohla být nalezena adresa. Některé přílohy nelze přeposlat, protože ještě nebyly staženy. diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 97e257595..99d464f29 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -263,7 +263,7 @@ Vær venlig at sende fejlrapporter, anmodning om nye funktioner, og spørgsmål Fra: Til: Cc: - %s skrev:\n + %s skrev: Du skal angive mindst én modtager. Mail addresse ikke fundet. Nogle vedhæftninger kunne ikke videresendes fordi de ikke er blevet hentet fra server. diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index d5daf6d70..675968574 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -263,7 +263,7 @@ Um Fehler zu melden, neue Funktionen vorzuschlagen oder Fragen zu stellen, besuc Von: An: CC: - \n\n%s schrieb:\n + %s schrieb: Sie müssen mindestens einen Empfänger wählen. Es wurde keine E-Mail-Adresse für diesen Kontakt gefunden. Einige Anhänge können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden. diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index abb4d655a..9c321b120 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -258,7 +258,7 @@ Από: Προς: Κοιν: - %s έγραψε:\n + %s έγραψε: Πρέπει να συμπεριλάβετε τουλάχιστον έναν παραλήπτη. Δεν βρέθηκαν διευθύνσεις ηλεκτρονικού ταχυδρομείου. Μερικά συνημμένα δεν μπορούν να προωθηθούν γιατί δεν έχουν κατεβεί. diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index afe303f7f..fcf883598 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -262,7 +262,7 @@ Por favor, envía los errores detectados, contribuye con nuevas funcionalidades De: Para: CC: - %s escribió:\n + %s escribió: Debe añadir al menos un destinatario Dirección e-mail no encontrada. Algunos adjuntos no pueden reenviarse porque no han sido descargados. diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 3281c9806..51f405f39 100755 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -264,7 +264,7 @@ Virheraportit, osallistuminen projektiin ja kysymykset: Mene osoitteeseen Lähettäjä: Vastaanottaja: Kopio: - %s kirjoitti:\n + %s kirjoitti: Valitse vähintään yksi vastaanottaja. Tämän henkilön sähköpostiosoitetta ei löytynyt. Joitakin liitteitä ei voida lähettää edelleen, koska niitä ei ole ladattu. diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index a3b381036..82c319b0b 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -288,7 +288,7 @@ de plus De\u00A0: À\u00A0: Cc\u00A0: - \n%s a écrit\u00A0:\n + %s a écrit\u00A0: Vous devez ajouter au moins un destinataire. Aucune adresse e-mail trouvée Certaines pièces jointes ne peuvent pas être transmises car elles n\'ont pas été téléchargées. diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index b050dd73a..6877fdb06 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -262,7 +262,7 @@ Por favor, envía os erros detectados, contribúe con novas funcionalidas e preg Dende: Para: CC: - %s escribiu:\n + %s escribiu: Debes engadir un destinatario Non atopo enderezo electrónico. Algúns adxuntos non poden reenviarse porque non foron descargados. diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 0f7deab9a..ad0e2294b 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -263,7 +263,7 @@ Hibajelentéseivel hozzájárul az újabb verziók tökéletesítéséhez, kérd Feladó: Címzett: Másolat: - %s írta:\n + %s írta: Legalább egy címzetted adjon meg. E-mail cím nem található. Néhány mellékletet nem lehet továbbítani, mert nem lettek letöltve. diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 0f82facca..d45261e94 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -263,7 +263,7 @@ Invia le tue segnalazioni, suggerisci nuove funzionalità e chiedi informazioni Da: A: Cc: - %s ha scritto:\n + %s ha scritto: È necessario specificare almeno un destinatario. Nessun indirizzo email trovato. Alcuni allegati non possono essere inoltrati in quanto non sono stati scaricati. diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 56b219667..c192b2c7c 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -263,7 +263,7 @@ מ: ל: Cc: - %s כותב:\n + %s כותב: עליך להוסיף לפחות נמען אחד. לא ניתן למצוא כתובת דוא\"ל. קבצים מסוימים אינם ניתנים להעברה בגלל שהם לא הורדו. diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 547b0ce1f..16767ea40 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -262,7 +262,7 @@ K-9 は大多数のメールクライアントと同様に、ほとんどのフ 送信者: 宛先: CC: - %s wrote:\n + %s wrote: 少なくとも1つの受信者を追加する必要があります メールアドレスが登録されていません ダウンロードしていないため、一部の添付ファイルを転送することはできません。 diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index dac382446..a853e6b2a 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -261,7 +261,7 @@ K-9 메일은 대부분의 무료 hotmail 계정을 지원하지 않으며, 다 보낸 사람: 받는 사람: 참조: - %s이 씀:\n + %s이 씀: 한 명 이상의 받는 사람을 입력하십시오. 이메일 주소를 찾을 수 없습니다. 일부 첨부 파일이 다운로드되지 않아 전달될 수 없습니다. diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 4ee7a0fe7..b5a416905 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -262,7 +262,7 @@ Graag foutrapporten, bijdrage nieuwe functies en vragen stellen op Van: Aan: CC: - %s schreef:\n + %s schreef: Minimaal 1 ontvanger kiezen. Geen e-mailadres gevonden. Sommige bijlagen kunnen niet worden doorgestuurd omdat ze niet zijn gedownload. diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index c8f955a02..78b74de6f 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -268,7 +268,7 @@ Wszelkie zgłoszenia usterek, zapytania oraz nowe pomysły prosimy przesyłać z Od: Do: DW: - %s napisał:\n + %s napisał: Musisz dodać co najmniej jednego odbiorcę. Żaden adres email nie został znaleziony. Niektóre załączniki nie mogą być przesłane dalej ponieważ nie zostały wcześniej pobrane. diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml index a29ca215d..2344b2431 100644 --- a/res/values-pt-rBR/strings.xml +++ b/res/values-pt-rBR/strings.xml @@ -259,7 +259,7 @@ Por favor, nos envie relatórios de bugs, contribua para novas melhorias e faça De: Para: CC: - %s escreveu:\n + %s escreveu: Você deve incluir ao menos um destinatário. Não foi possível encontrar o endereço de e-mail. Alguns anexos não podem ser encaminhados porque não foram inclusos na mensagem. diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 86ee55f4a..cb3076ec2 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -258,7 +258,7 @@ K-9 Mail — почтовый клиент для Android. От: Кому: Копия: - %s пишет:\n + %s пишет: Укажите адресата Контакт не содержит сведений о email Некоторые вложения не были загружены и не могут быть отправлены diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 2317ec58d..948793647 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -263,7 +263,7 @@ Prosím nahlasujte prípadné chyby, prispievajte novými funkciami a pýtajte s Od: Komu: Kópia: - %s napísal(-a):\n + %s napísal(-a): Musíte pridať aspoň jedného príjemcu. Nebola nájdená žiadna e-mailová adresa pre tento kontakt. Niektoré prílohy nemožno preposlať, pretože neboli stiahnuté. diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 67c88a8b4..586cf40b6 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -263,7 +263,7 @@ Vänligen skicka felrapporter, hjälp till med nya funktioner och ställ frågor Från: Till: CC: - %s skrev:\n + %s skrev: Du måste ange åtminstone en mottagare. Kan inte hitta e-postadress för denna kontakt. Några bilagor kan inte vidarebefordras eftersom de inte har hämtats. diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index b5e556d53..b01e8b92c 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -263,7 +263,7 @@ Lütfen hata raporlarınızı, istediğiniz yeni özellikleri ve sorularınızı Kimden: Alıcı: Cc: - %s yazdı:\n + %s yazdı: En az bir Alıcı eklemelisiniz. E-posta adresi bulunamadı. Bazı ekler iletilemedi çünkü indirilemedi. diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index e3ed03921..006700379 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -263,7 +263,7 @@ K-9 Mail це поштовий клієнт з відкритим вихідни Від: Кому: Копія: - %s написав(ла):\n + %s написав(ла): Вам необхідно додати хоча б одного одержувача. Не знайденої адреси електронної пошти. Деякі вкладення не можуть бути переслані бо вони не завантажилися. diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index d9607b92f..5fa2e733f 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -256,7 +256,7 @@ K-9改进的功能包括: 发件人: 收件人: 抄送: - %s写到:\n + %s写到: 您必须添加至少一个收件人。 没有找到收件地址。 由于一些附件还没有被下载,因此无法转发这些附件。 diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 22c0fea7b..b4328c72b 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -230,9 +230,7 @@ 寄件人: 收件人: 副本: - "%s寫到: - -" + %s寫到: 至少必須加入一位收件人。 沒有發現電子郵件地址 由於一些附件還沒有被下載,因此無法轉寄這些附件。 diff --git a/res/values/strings.xml b/res/values/strings.xml index af4b073a9..e1cafb5d4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -268,7 +268,7 @@ Please submit bug reports, contribute new features and ask questions at From: To: Cc: - %s wrote:\n + %s wrote: You must add at least one recipient. No email address could be found for this contact. Some attachments cannot be forwarded because they have not been downloaded. diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index ae4827648..f6451fbd4 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -19,6 +19,7 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.text.TextWatcher; import android.text.util.Rfc822Tokenizer; +import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; @@ -273,14 +274,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, private MultiAutoCompleteTextView mCcView; private MultiAutoCompleteTextView mBccView; private EditText mSubjectView; - private EditText mSignatureView; - private EditText mMessageContentView; + private EolConvertingEditText mSignatureView; + private EolConvertingEditText mMessageContentView; private LinearLayout mAttachments; private Button mQuotedTextShow; private View mQuotedTextBar; private ImageButton mQuotedTextEdit; private ImageButton mQuotedTextDelete; - private EditText mQuotedText; + private EolConvertingEditText mQuotedText; private MessageWebView mQuotedHTML; private InsertableHtmlContent mQuotedHtmlContent; // Container for HTML reply as it's being built. private View mEncryptLayout; @@ -588,10 +589,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, onAddCcBcc(); } - EditText upperSignature = (EditText)findViewById(R.id.upper_signature); - EditText lowerSignature = (EditText)findViewById(R.id.lower_signature); + EolConvertingEditText upperSignature = (EolConvertingEditText)findViewById(R.id.upper_signature); + EolConvertingEditText lowerSignature = (EolConvertingEditText)findViewById(R.id.lower_signature); - mMessageContentView = (EditText)findViewById(R.id.message_content); + mMessageContentView = (EolConvertingEditText)findViewById(R.id.message_content); mMessageContentView.getInputExtras(true).putBoolean("allowEmoji", true); mAttachments = (LinearLayout)findViewById(R.id.attachments); @@ -599,7 +600,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, mQuotedTextBar = findViewById(R.id.quoted_text_bar); mQuotedTextEdit = (ImageButton)findViewById(R.id.quoted_text_edit); mQuotedTextDelete = (ImageButton)findViewById(R.id.quoted_text_delete); - mQuotedText = (EditText)findViewById(R.id.quoted_text); + mQuotedText = (EolConvertingEditText)findViewById(R.id.quoted_text); mQuotedText.getInputExtras(true).putBoolean("allowEmoji", true); mQuotedHTML = (MessageWebView) findViewById(R.id.quoted_html); @@ -949,7 +950,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT); // Only use EXTRA_TEXT if the body hasn't already been set by the mailto URI if (text != null && mMessageContentView.getText().length() == 0) { - mMessageContentView.setText(text); + mMessageContentView.setCharacters(text); } String type = intent.getType(); @@ -1307,7 +1308,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, boolean signatureBeforeQuotedText = mAccount.isSignatureBeforeQuotedText(); // Get the user-supplied text - String text = mMessageContentView.getText().toString(); + String text = mMessageContentView.getCharacters(); // Handle HTML separate from the rest of the text content if (messageFormat == SimpleMessageFormat.HTML) { @@ -1390,13 +1391,13 @@ public class MessageCompose extends K9Activity implements OnClickListener, } } - if (includeQuotedText) { - String quotedText = mQuotedText.getText().toString(); + String quotedText = mQuotedText.getCharacters(); + if (includeQuotedText && quotedText.length() > 0) { if (replyAfterQuote) { - composedMessageOffset = quotedText.length() + "\n".length(); - text = quotedText + "\n" + text; + composedMessageOffset = quotedText.length() + "\r\n".length(); + text = quotedText + "\r\n" + text; } else { - text += "\n\n" + quotedText.toString(); + text += "\r\n\r\n" + quotedText.toString(); } } @@ -1540,7 +1541,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, * header value (all parameters at once) will be encoded by * MimeHeader.writeTo(). */ - bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"", + bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\r\n name=\"%s\"", contentType, EncoderUtil.encodeIfNecessary(attachment.name, EncoderUtil.Usage.WORD_ENTITY, 7))); @@ -1563,7 +1564,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, * title*3="isn't it!" */ bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format( - "attachment;\n filename=\"%s\";\n size=%d", + "attachment;\r\n filename=\"%s\";\r\n size=%d", attachment.name, attachment.size)); mp.addBodyPart(bp); @@ -1661,7 +1662,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // If we're not using the standard identity of signature, append it on to the identity blob. if (mIdentity.getSignatureUse() && mSignatureChanged) { - uri.appendQueryParameter(IdentityField.SIGNATURE.value(), mSignatureView.getText().toString()); + uri.appendQueryParameter(IdentityField.SIGNATURE.value(), mSignatureView.getCharacters()); } if (mIdentityChanged) { @@ -1766,10 +1767,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, private String appendSignature(String originalText) { String text = originalText; if (mIdentity.getSignatureUse()) { - String signature = mSignatureView.getText().toString(); + String signature = mSignatureView.getCharacters(); if (signature != null && !signature.contentEquals("")) { - text += "\n" + signature; + text += "\r\n" + signature; } } @@ -1783,9 +1784,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, private String getSignatureHtml() { String signature = ""; if (mIdentity.getSignatureUse()) { - signature = mSignatureView.getText().toString(); + signature = mSignatureView.getCharacters(); if(!StringUtils.isNullOrEmpty(signature)) { - signature = HtmlConverter.textToHtmlFragment("\n" + signature); + signature = HtmlConverter.textToHtmlFragment("\r\n" + signature); } } return signature; @@ -2320,7 +2321,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, private void updateSignature() { if (mIdentity.getSignatureUse()) { - mSignatureView.setText(mIdentity.getSignature()); + mSignatureView.setCharacters(mIdentity.getSignature()); mSignatureView.setVisibility(View.VISIBLE); } else { mSignatureView.setVisibility(View.GONE); @@ -3006,7 +3007,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // 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 // match the behavior in text mode. - mMessageContentView.setText(getBodyTextFromMessage(message, SimpleMessageFormat.TEXT)); + mMessageContentView.setCharacters(getBodyTextFromMessage(message, SimpleMessageFormat.TEXT)); mForcePlainText = true; showOrHideQuotedText(quotedMode); @@ -3023,9 +3024,15 @@ public class MessageCompose extends K9Activity implements OnClickListener, Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); } + if (bodyOffset + bodyLength > text.length()) { + // The draft was edited outside of K-9 Mail? + Log.d(K9.LOG_TAG, "The identity field from the draft contains an invalid LENGTH/OFFSET"); + bodyOffset = 0; + bodyLength = 0; + } // Grab our reply text. String bodyText = text.substring(bodyOffset, bodyOffset + bodyLength); - mMessageContentView.setText(HtmlConverter.htmlToText(bodyText)); + mMessageContentView.setCharacters(HtmlConverter.htmlToText(bodyText)); // Regenerate the quoted html without our user content in it. StringBuilder quotedHTML = new StringBuilder(); @@ -3064,7 +3071,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, showOrHideQuotedText(quotedMode); } - /* + /** * 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. * @param message Source message @@ -3084,27 +3091,40 @@ public class MessageCompose extends K9Activity implements OnClickListener, // If we had a body length (and it was valid), separate the composition from the quoted text // and put them in their respective places in the UI. - if (bodyLength != null && bodyLength + 1 < text.length()) { // + 1 to get rid of the newline we added when saving the draft - String bodyText = text.substring(bodyOffset, bodyOffset + bodyLength); + if (bodyLength > 0) { + try { + String bodyText = text.substring(bodyOffset, bodyOffset + bodyLength); - // Regenerate the quoted text without our user content in it nor added newlines. - StringBuilder quotedText = new StringBuilder(); - if (bodyOffset == 0 && text.substring(bodyLength, bodyLength + 2).equals("\n\n")) { - // top-posting: ignore two newlines at start of quote - quotedText.append(text.substring(bodyLength + 2)); - } else if (bodyOffset + bodyLength == text.length() && - text.substring(bodyOffset - 1, bodyOffset).equals("\n")) { - // bottom-posting: ignore newline at end of quote - quotedText.append(text.substring(0, bodyOffset - 1)); - } else { - quotedText.append(text.substring(0, bodyOffset)); // stuff before the reply - quotedText.append(text.substring(bodyOffset + bodyLength)); + // Regenerate the quoted text without our user content in it nor added newlines. + StringBuilder quotedText = new StringBuilder(); + if (bodyOffset == 0 && text.substring(bodyLength, bodyLength + 4).equals("\r\n\r\n")) { + // top-posting: ignore two newlines at start of quote + quotedText.append(text.substring(bodyLength + 4)); + } else if (bodyOffset + bodyLength == text.length() && + text.substring(bodyOffset - 2, bodyOffset).equals("\r\n")) { + // bottom-posting: ignore newline at end of quote + quotedText.append(text.substring(0, bodyOffset - 2)); + } else { + quotedText.append(text.substring(0, bodyOffset)); // stuff before the reply + quotedText.append(text.substring(bodyOffset + bodyLength)); + } + + if (viewMessageContent) { + mMessageContentView.setCharacters(bodyText); + } + + mQuotedText.setCharacters(quotedText); + } catch (IndexOutOfBoundsException e) { + // Invalid bodyOffset or bodyLength. The draft was edited outside of K-9 Mail? + Log.d(K9.LOG_TAG, "The identity field from the draft contains an invalid bodyOffset/bodyLength"); + if (viewMessageContent) { + mMessageContentView.setCharacters(text); + } } - - if (viewMessageContent) mMessageContentView.setText(bodyText); - mQuotedText.setText(quotedText.toString()); } else { - if (viewMessageContent) mMessageContentView.setText(text); + if (viewMessageContent) { + mMessageContentView.setCharacters(text); + } } } } @@ -3228,7 +3248,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent()); // TODO: Also strip the signature from the text/plain part - mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage, + mQuotedText.setCharacters(quoteOriginalTextMessage(mSourceMessage, getBodyTextFromMessage(mSourceMessage, SimpleMessageFormat.TEXT), mQuoteStyle)); } else if (mQuotedTextFormat == SimpleMessageFormat.TEXT) { @@ -3239,7 +3259,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, } } - mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage, content, mQuoteStyle)); + mQuotedText.setCharacters(quoteOriginalTextMessage(mSourceMessage, content, mQuoteStyle)); } if (showQuotedText) { @@ -3308,7 +3328,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, private static final int FIND_INSERTION_POINT_FIRST_GROUP = 1; // HTML bits to insert as appropriate // TODO is it safe to assume utf-8 here? - private static final String FIND_INSERTION_POINT_HTML_CONTENT = "\n"; + private static final String FIND_INSERTION_POINT_HTML_CONTENT = "\r\n"; private static final String FIND_INSERTION_POINT_HTML_END_CONTENT = ""; private static final String FIND_INSERTION_POINT_HEAD_CONTENT = ""; // Index of the start of the beginning of a String. @@ -3558,7 +3578,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // Read message body from the "body" parameter. List body = uri.getQueryParameters("body"); if (!body.isEmpty()) { - mMessageContentView.setText(body.get(0)); + mMessageContentView.setCharacters(body.get(0)); } } @@ -3686,7 +3706,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, if (quoteStyle == QuoteStyle.PREFIX) { StringBuilder quotedText = new StringBuilder(body.length() + QUOTE_BUFFER_LENGTH); quotedText.append(String.format( - getString(R.string.message_compose_reply_header_fmt), + getString(R.string.message_compose_reply_header_fmt) + "\r\n", Address.toString(originalMessage.getFrom())) ); @@ -3701,24 +3721,24 @@ public class MessageCompose extends K9Activity implements OnClickListener, return quotedText.toString().replaceAll("\\\r", ""); } else if (quoteStyle == QuoteStyle.HEADER) { StringBuilder quotedText = new StringBuilder(body.length() + QUOTE_BUFFER_LENGTH); - quotedText.append("\n"); - quotedText.append(getString(R.string.message_compose_quote_header_separator)).append("\n"); + quotedText.append("\r\n"); + quotedText.append(getString(R.string.message_compose_quote_header_separator)).append("\r\n"); if (originalMessage.getFrom() != null && Address.toString(originalMessage.getFrom()).length() != 0) { - quotedText.append(getString(R.string.message_compose_quote_header_from)).append(" ").append(Address.toString(originalMessage.getFrom())).append("\n"); + quotedText.append(getString(R.string.message_compose_quote_header_from)).append(" ").append(Address.toString(originalMessage.getFrom())).append("\r\n"); } if (originalMessage.getSentDate() != null) { - quotedText.append(getString(R.string.message_compose_quote_header_send_date)).append(" ").append(originalMessage.getSentDate()).append("\n"); + quotedText.append(getString(R.string.message_compose_quote_header_send_date)).append(" ").append(originalMessage.getSentDate()).append("\r\n"); } if (originalMessage.getRecipients(RecipientType.TO) != null && originalMessage.getRecipients(RecipientType.TO).length != 0) { - quotedText.append(getString(R.string.message_compose_quote_header_to)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.TO))).append("\n"); + quotedText.append(getString(R.string.message_compose_quote_header_to)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.TO))).append("\r\n"); } if (originalMessage.getRecipients(RecipientType.CC) != null && originalMessage.getRecipients(RecipientType.CC).length != 0) { - quotedText.append(getString(R.string.message_compose_quote_header_cc)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.CC))).append("\n"); + quotedText.append(getString(R.string.message_compose_quote_header_cc)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.CC))).append("\r\n"); } if (originalMessage.getSubject() != null) { - quotedText.append(getString(R.string.message_compose_quote_header_subject)).append(" ").append(originalMessage.getSubject()).append("\n"); + quotedText.append(getString(R.string.message_compose_quote_header_subject)).append(" ").append(originalMessage.getSubject()).append("\r\n"); } - quotedText.append("\n"); + quotedText.append("\r\n"); quotedText.append(body); @@ -3743,13 +3763,12 @@ public class MessageCompose extends K9Activity implements OnClickListener, if (quoteStyle == QuoteStyle.PREFIX) { StringBuilder header = new StringBuilder(QUOTE_BUFFER_LENGTH); header.append("
"); - // Remove all trailing newlines so that the quote starts immediately after the header. "Be like Gmail!" header.append(HtmlConverter.textToHtmlFragment(String.format( - getString(R.string.message_compose_reply_header_fmt).replaceAll("\n$", ""), + getString(R.string.message_compose_reply_header_fmt), Address.toString(originalMessage.getFrom())) )); header.append("
\n"); + "style=\"margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;\">\r\n"); String footer = "
"; @@ -3758,35 +3777,35 @@ public class MessageCompose extends K9Activity implements OnClickListener, } else if (quoteStyle == QuoteStyle.HEADER) { StringBuilder header = new StringBuilder(); - header.append("
\n"); - header.append("
\n"); // This gets converted into a horizontal line during html to text conversion. + header.append("
\r\n"); + header.append("
\r\n"); // This gets converted into a horizontal line during html to text conversion. if (mSourceMessage.getFrom() != null && Address.toString(mSourceMessage.getFrom()).length() != 0) { header.append("").append(getString(R.string.message_compose_quote_header_from)).append(" ") .append(HtmlConverter.textToHtmlFragment(Address.toString(mSourceMessage.getFrom()))) - .append("
\n"); + .append("
\r\n"); } if (mSourceMessage.getSentDate() != null) { header.append("").append(getString(R.string.message_compose_quote_header_send_date)).append(" ") .append(mSourceMessage.getSentDate()) - .append("
\n"); + .append("
\r\n"); } if (mSourceMessage.getRecipients(RecipientType.TO) != null && mSourceMessage.getRecipients(RecipientType.TO).length != 0) { header.append("").append(getString(R.string.message_compose_quote_header_to)).append(" ") .append(HtmlConverter.textToHtmlFragment(Address.toString(mSourceMessage.getRecipients(RecipientType.TO)))) - .append("
\n"); + .append("
\r\n"); } if (mSourceMessage.getRecipients(RecipientType.CC) != null && mSourceMessage.getRecipients(RecipientType.CC).length != 0) { header.append("").append(getString(R.string.message_compose_quote_header_cc)).append(" ") .append(HtmlConverter.textToHtmlFragment(Address.toString(mSourceMessage.getRecipients(RecipientType.CC)))) - .append("
\n"); + .append("
\r\n"); } if (mSourceMessage.getSubject() != null) { header.append("").append(getString(R.string.message_compose_quote_header_subject)).append(" ") .append(HtmlConverter.textToHtmlFragment(mSourceMessage.getSubject())) - .append("
\n"); + .append("
\r\n"); } - header.append("
\n"); - header.append("
\n"); + header.append("
\r\n"); + header.append("
\r\n"); insertable.insertIntoQuotedHeader(header.toString()); } @@ -3970,4 +3989,35 @@ public class MessageCompose extends K9Activity implements OnClickListener, private boolean includeQuotedText() { return (mQuotedTextMode == QuotedTextMode.SHOW); } + + /** + * An {@link EditText} extension with methods that convert line endings from + * {@code \r\n} to {@code \n} and back again when setting and getting text. + * + */ + private static class EolConvertingEditText extends EditText { + + public EolConvertingEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Return the text the EolConvertingEditText is displaying. + * + * @return A string with any line endings converted to {@code \r\n}. + */ + public String getCharacters() { + return getText().toString().replace("\n", "\r\n"); + } + + /** + * Sets the string value of the EolConvertingEditText. Any line endings + * in the string will be converted to {@code \n}. + * + * @param text + */ + public void setCharacters(CharSequence text) { + setText(text.toString().replace("\r\n", "\n")); + } + } } diff --git a/src/com/fsck/k9/helper/HtmlConverter.java b/src/com/fsck/k9/helper/HtmlConverter.java index 275e50820..e17d3ed23 100644 --- a/src/com/fsck/k9/helper/HtmlConverter.java +++ b/src/com/fsck/k9/helper/HtmlConverter.java @@ -70,7 +70,7 @@ public class HtmlConverter { if (tag.equals("hr") && opening) { // In the case of an
, replace it with a bunch of underscores. This is roughly // the behaviour of Outlook in Rich Text mode. - output.append("_____________________________________________\n"); + output.append("_____________________________________________\r\n"); } else if (TAGS_WITH_IGNORED_CONTENT.contains(tag)) { handleIgnoredTag(opening, output); } @@ -282,11 +282,16 @@ public class HtmlConverter { // Replace lines of -,= or _ with horizontal rules text = text.replaceAll("\\s*([-=_]{30,}+)\\s*", "
"); - // TODO: reverse engineer (or troll history) and document + /* + * Unwrap multi-line paragraphs into single line paragraphs that are + * wrapped when displayed. But try to avoid unwrapping consecutive lines + * of text that are not paragraphs, such as lists of system log entries + * or long URLs that are on their own line. + */ text = text.replaceAll("(?m)^([^\r\n]{4,}[\\s\\w,:;+/])(?:\r\n|\n|\r)(?=[a-z]\\S{0,10}[\\s\\n\\r])", "$1 "); // Compress four or more newlines down to two newlines - text = text.replaceAll("(?m)(\r\n|\n|\r){4,}", "\n\n"); + text = text.replaceAll("(?m)(\r\n|\n|\r){4,}", "\r\n\r\n"); StringBuffer sb = new StringBuffer(text.length() + TEXT_TO_HTML_EXTRA_BUFFER_LENGTH); @@ -1315,7 +1320,7 @@ public class HtmlConverter { // // For some reason, TextUtils.htmlEncode escapes ' into ', which is technically part of the XHTML 1.0 // standard, but Gmail doesn't recognize it as an HTML entity. We unescape that here. - return linkified.toString().replace("\n", "
\n").replace("'", "'"); + return linkified.toString().replaceAll("\r?\n", "
\r\n").replace("'", "'"); } /** @@ -1345,10 +1350,10 @@ public class HtmlConverter { lastChar = output.charAt(output.length() - 1); } if (lastChar != '\n') { - output.append("\n"); + output.append("\r\n"); } } else { - output.append("\n"); + output.append("\r\n"); } } @@ -1356,7 +1361,7 @@ public class HtmlConverter { if (opening) { output.append("\t• "); } else { - output.append("\n"); + output.append("\r\n"); } } } diff --git a/src/com/fsck/k9/helper/Utility.java b/src/com/fsck/k9/helper/Utility.java index 67646806d..b8eef469f 100644 --- a/src/com/fsck/k9/helper/Utility.java +++ b/src/com/fsck/k9/helper/Utility.java @@ -282,7 +282,7 @@ public class Utility { StringBuilder result = new StringBuilder(); for (String piece : str.split(NEWLINE_REGEX)) { result.append(wrap(piece, wrapLength, null, false)); - result.append("\n"); + result.append("\r\n"); } return result.toString(); } @@ -330,7 +330,7 @@ public class Utility { return null; } if (newLineStr == null) { - newLineStr = "\n"; + newLineStr = "\r\n"; } if (wrapLength < 1) { wrapLength = 1; diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index c80100389..a43efff21 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -1732,7 +1732,7 @@ public class MimeUtility { text.append(context.getString(R.string.message_compose_quote_header_from)); text.append(' '); text.append(Address.toString(from)); - text.append("\n"); + text.append("\r\n"); } // To: @@ -1741,7 +1741,7 @@ public class MimeUtility { text.append(context.getString(R.string.message_compose_quote_header_to)); text.append(' '); text.append(Address.toString(to)); - text.append("\n"); + text.append("\r\n"); } // Cc: @@ -1750,7 +1750,7 @@ public class MimeUtility { text.append(context.getString(R.string.message_compose_quote_header_cc)); text.append(' '); text.append(Address.toString(cc)); - text.append("\n"); + text.append("\r\n"); } // Date: @@ -1759,7 +1759,7 @@ public class MimeUtility { text.append(context.getString(R.string.message_compose_quote_header_send_date)); text.append(' '); text.append(date.toString()); - text.append("\n"); + text.append("\r\n"); } // Subject: @@ -1771,7 +1771,7 @@ public class MimeUtility { } else { text.append(subject); } - text.append("\n\n"); + text.append("\r\n\r\n"); } /** @@ -1919,7 +1919,7 @@ public class MimeUtility { if (prependDivider) { String filename = getPartName(part); - text.append("\n\n"); + text.append("\r\n\r\n"); int len = filename.length(); if (len > 0) { if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) { @@ -1934,7 +1934,7 @@ public class MimeUtility { } else { text.append(TEXT_DIVIDER); } - text.append("\n\n"); + text.append("\r\n\r\n"); } } @@ -3416,7 +3416,7 @@ public class MimeUtility { if (part.isMimeType("text/plain")) { String bodyText = getTextFromPart(part); if (bodyText != null) { - text = fixDraftTextBody(bodyText); + text = bodyText; html = HtmlConverter.textToHtml(text); } } else if (part.isMimeType("multipart/alternative") && @@ -3427,9 +3427,9 @@ public class MimeUtility { String bodyText = getTextFromPart(bodyPart); if (bodyText != null) { if (text.length() == 0 && bodyPart.isMimeType("text/plain")) { - text = fixDraftTextBody(bodyText); + text = bodyText; } else if (html.length() == 0 && bodyPart.isMimeType("text/html")) { - html = fixDraftTextBody(bodyText); + html = bodyText; } } } @@ -3437,21 +3437,4 @@ public class MimeUtility { return new ViewableContainer(text, html, attachments); } - - /** - * Fix line endings of text bodies in draft messages. - * - *

- * We create drafts with LF line endings. The values in the identity header are based on that. - * So we replace CRLF with LF when loading messages (from the server). - *

- * - * @param text - * The body text with CRLF line endings - * - * @return The text with LF line endings - */ - private static String fixDraftTextBody(String text) { - return text.replace("\r\n", "\n"); - } } diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index e2bbfa0f1..fd1876558 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -1917,7 +1917,7 @@ public class ImapStore extends Store { * of them. */ for (int i = 0, count = bodyParams.size(); i < count; i += 2) { - contentType.append(String.format(";\n %s=\"%s\"", + contentType.append(String.format(";\r\n %s=\"%s\"", bodyParams.getString(i), bodyParams.getString(i + 1))); } @@ -1952,7 +1952,7 @@ public class ImapStore extends Store { * about the attachment out. */ for (int i = 0, count = bodyDispositionParams.size(); i < count; i += 2) { - contentDisposition.append(String.format(";\n %s=\"%s\"", + contentDisposition.append(String.format(";\r\n %s=\"%s\"", bodyDispositionParams.getString(i).toLowerCase(Locale.US), bodyDispositionParams.getString(i + 1))); } @@ -1960,7 +1960,7 @@ public class ImapStore extends Store { } if (MimeUtility.getHeaderParameter(contentDisposition.toString(), "size") == null) { - contentDisposition.append(String.format(";\n size=%d", size)); + contentDisposition.append(String.format(";\r\n size=%d", size)); } /* diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 0d16a5210..c66cc5e44 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -1964,18 +1964,18 @@ public class LocalStore extends Store implements Serializable { bp.setEncoding(encoding); if (name != null) { bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, - String.format("%s;\n name=\"%s\"", + String.format("%s;\r\n name=\"%s\"", type, name)); bp.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, - String.format("%s;\n filename=\"%s\";\n size=%d", + String.format("%s;\r\n filename=\"%s\";\r\n size=%d", contentDisposition, name, // TODO: Should use encoded word defined in RFC 2231. size)); } else { bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, type); bp.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, - String.format("%s;\n size=%d", + String.format("%s;\r\n size=%d", contentDisposition, size)); } diff --git a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java index e5477be78..ed0c876c9 100644 --- a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java +++ b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java @@ -12,16 +12,16 @@ public class HtmlConverterTest extends TestCase { private static final String OUTPUT_FILE = "C:/temp/parse.html"; public void testTextQuoteToHtmlBlockquote() { - String message = "Panama!\n" + - "\n" + - "Bob Barker wrote:\n" + - "> a canal\n" + - ">\n" + - "> Dorothy Jo Gideon espoused:\n" + - "> >A man, a plan...\n" + - "> Too easy!\n" + - "\n" + - "Nice job :)\n" + + String message = "Panama!\r\n" + + "\r\n" + + "Bob Barker wrote:\r\n" + + "> a canal\r\n" + + ">\r\n" + + "> Dorothy Jo Gideon espoused:\r\n" + + "> >A man, a plan...\r\n" + + "> Too easy!\r\n" + + "\r\n" + + "Nice job :)\r\n" + ">> Guess!"; String result = HtmlConverter.textToHtml(message); writeToFile(result); @@ -49,13 +49,13 @@ public class HtmlConverterTest extends TestCase { } public void testTextQuoteToHtmlBlockquoteIndented() { - String message = "*facepalm*\n" + - "\n" + - "Bob Barker wrote:\n" + - "> A wise man once said...\n" + - ">\n" + - "> LOL F1RST!!!!!\n" + - ">\n" + + String message = "*facepalm*\r\n" + + "\r\n" + + "Bob Barker wrote:\r\n" + + "> A wise man once said...\r\n" + + ">\r\n" + + "> LOL F1RST!!!!!\r\n" + + ">\r\n" + "> :)"; String result = HtmlConverter.textToHtml(message); writeToFile(result); @@ -84,12 +84,12 @@ public class HtmlConverterTest extends TestCase { assertEquals(HtmlConverter.getQuoteColor(0), HtmlConverter.QUOTE_COLOR_DEFAULT); assertEquals(HtmlConverter.getQuoteColor(6), HtmlConverter.QUOTE_COLOR_DEFAULT); - String message = "zero\n" + - "> one\n" + - ">> two\n" + - ">>> three\n" + - ">>>> four\n" + - ">>>>> five\n" + + String message = "zero\r\n" + + "> one\r\n" + + ">> two\r\n" + + ">>> three\r\n" + + ">>>> four\r\n" + + ">>>>> five\r\n" + ">>>>>> six"; String result = HtmlConverter.textToHtml(message); writeToFile(result); @@ -136,9 +136,9 @@ public class HtmlConverterTest extends TestCase { } public void testPreserveSpacesAtFirst() { - String message = "foo\n" - + " bar\n" - + " baz\n"; + String message = "foo\r\n" + + " bar\r\n" + + " baz\r\n"; String result = HtmlConverter.textToHtml(message); writeToFile(result); assertEquals("
"
@@ -150,11 +150,11 @@ public class HtmlConverterTest extends TestCase {
 
     public void testPreserveSpacesAtFirstForSpecialCharacters() {
         String message =
-                  " \n"
-                + "  &\n"
-                + "    \r\n"
-                + "   <\n"
-                + "  > \n";
+                  " \r\n"
+                + "  &\r\n"
+                + "    \n"
+                + "   <\r\n"
+                + "  > \r\n";
         String result = HtmlConverter.textToHtml(message);
         writeToFile(result);
         assertEquals("
"
diff --git a/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java b/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java
index f10ad791a..cb62079f3 100644
--- a/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java
+++ b/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java
@@ -81,8 +81,8 @@ public class ViewablesTest extends AndroidTestCase {
         ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
 
         String expectedText =
-                bodyText1 + "\n\n" +
-                "------------------------------------------------------------------------\n\n" +
+                bodyText1 + "\r\n\r\n" +
+                "------------------------------------------------------------------------\r\n\r\n" +
                 bodyText2;
         String expectedHtml =
                 "
" +
@@ -138,14 +138,14 @@ public class ViewablesTest extends AndroidTestCase {
 
         String expectedText =
                 bodyText +
-                "\n\n" +
+                "\r\n\r\n" +
                 "----- message.eml ------------------------------------------------------" +
-                "\n\n" +
-                "From: from@example.com" + "\n" +
-                "To: to@example.com" + "\n" +
-                "Sent: Sat Mar 17 00:00:00 GMT+01:00 2012" + "\n" +
-                "Subject: Subject" + "\n" +
-                "\n" +
+                "\r\n\r\n" +
+                "From: from@example.com" + "\r\n" +
+                "To: to@example.com" + "\r\n" +
+                "Sent: Sat Mar 17 00:00:00 GMT+01:00 2012" + "\r\n" +
+                "Subject: Subject" + "\r\n" +
+                "\r\n" +
                 innerBodyText;
         String expectedHtml =
                 "
" +