auto import from //branches/cupcake_rel/...@140373
This commit is contained in:
parent
3b85e2c2b5
commit
3469902379
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Zpráva byla smazána."</string>
|
||||
<string name="message_discarded_toast">"Zpráva byla zrušena."</string>
|
||||
<string name="message_saved_toast">"Zpráva byla uložena jako koncept."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Nastavit e-mail"</string>
|
||||
<string name="account_setup_basics_instructions">"Zadejte e-mailovou adresu účtu:"</string>
|
||||
<string name="account_setup_basics_email_hint">"E-mailová adresa"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Nachricht gelöscht"</string>
|
||||
<string name="message_discarded_toast">"Nachricht gelöscht"</string>
|
||||
<string name="message_saved_toast">"Nachricht als Entwurf gespeichert"</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"E-Mail einrichten"</string>
|
||||
<string name="account_setup_basics_instructions">"Geben Sie Ihre im Konto gespeicherte E-Mail-Adresse ein:"</string>
|
||||
<string name="account_setup_basics_email_hint">"E-Mail-Adresse"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Mensaje suprimido"</string>
|
||||
<string name="message_discarded_toast">"Mensaje descartado"</string>
|
||||
<string name="message_saved_toast">"Mensaje guardado como borrador"</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Configurar correo electrónico"</string>
|
||||
<string name="account_setup_basics_instructions">"Introduce la dirección de correo electrónico de tu cuenta:"</string>
|
||||
<string name="account_setup_basics_email_hint">"Dirección de correo electrónico"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Message supprimé."</string>
|
||||
<string name="message_discarded_toast">"Message supprimé."</string>
|
||||
<string name="message_saved_toast">"Message enregistré comme brouillon."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Configurer la messagerie électronique"</string>
|
||||
<string name="account_setup_basics_instructions">"Saisissez l\'adresse e-mail de votre compte :"</string>
|
||||
<string name="account_setup_basics_email_hint">"Adresse e-mail"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Messaggio eliminato."</string>
|
||||
<string name="message_discarded_toast">"Messaggio eliminato."</string>
|
||||
<string name="message_saved_toast">"Messaggio salvato come bozza."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Imposta email"</string>
|
||||
<string name="account_setup_basics_instructions">"Digita l\'indirizzo email del tuo account:"</string>
|
||||
<string name="account_setup_basics_email_hint">"Indirizzo email"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"メッセージを削除しました。"</string>
|
||||
<string name="message_discarded_toast">"メッセージを破棄しました。"</string>
|
||||
<string name="message_saved_toast">"メッセージを下書きとして保存しました。"</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"メールアカウントの登録"</string>
|
||||
<string name="account_setup_basics_instructions">"メールのアカウント情報を入力:"</string>
|
||||
<string name="account_setup_basics_email_hint">"メールアドレス"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"메일이 삭제되었습니다."</string>
|
||||
<string name="message_discarded_toast">"메일이 삭제되었습니다."</string>
|
||||
<string name="message_saved_toast">"메일을 임시로 저장했습니다."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"이메일 설정"</string>
|
||||
<string name="account_setup_basics_instructions">"계정 이메일 주소 입력:"</string>
|
||||
<string name="account_setup_basics_email_hint">"이메일 주소"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Meldingen ble slettet."</string>
|
||||
<string name="message_discarded_toast">"Meldingen ble forkastet."</string>
|
||||
<string name="message_saved_toast">"Meldingen ble lagret som utkast."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Sett opp e-post"</string>
|
||||
<string name="account_setup_basics_instructions">"Skriv e-postadressen til kontoen din:"</string>
|
||||
<string name="account_setup_basics_email_hint">"E-postadresse"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Bericht verwijderd."</string>
|
||||
<string name="message_discarded_toast">"Bericht wordt verwijderd"</string>
|
||||
<string name="message_saved_toast">"Bericht opgeslagen als concept."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"E-mail instellen"</string>
|
||||
<string name="account_setup_basics_instructions">"Typ het e-mailadres van je account:"</string>
|
||||
<string name="account_setup_basics_email_hint">"E-mailadres"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Wiadomość została usunięta."</string>
|
||||
<string name="message_discarded_toast">"Wiadomość została odrzucona."</string>
|
||||
<string name="message_saved_toast">"Wiadomość została zapisana jako wersja robocza."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Skonfiguruj konto e-mail"</string>
|
||||
<string name="account_setup_basics_instructions">"Podaj adres e-mail swojego konta:"</string>
|
||||
<string name="account_setup_basics_email_hint">"Adres e-mail"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"Письмо удалено."</string>
|
||||
<string name="message_discarded_toast">"Письмо не сохранено."</string>
|
||||
<string name="message_saved_toast">"Письмо сохранено как черновик."</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"Настройка электронной почты"</string>
|
||||
<string name="account_setup_basics_instructions">"Укажите почтовый адрес своего аккаунта:"</string>
|
||||
<string name="account_setup_basics_email_hint">"Адрес электронной почты"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"邮件已删除。"</string>
|
||||
<string name="message_discarded_toast">"邮件已取消。"</string>
|
||||
<string name="message_saved_toast">"邮件已另存为草稿。"</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"设置电子邮件"</string>
|
||||
<string name="account_setup_basics_instructions">"键入您的帐户电子邮件地址:"</string>
|
||||
<string name="account_setup_basics_email_hint">"电子邮件地址"</string>
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
<string name="message_deleted_toast">"已刪除郵件。"</string>
|
||||
<string name="message_discarded_toast">"已捨棄郵件。"</string>
|
||||
<string name="message_saved_toast">"已儲存郵件草稿。"</string>
|
||||
<!-- no translation found for add_contact_dlg_title (7424641029188006580) -->
|
||||
<skip />
|
||||
<!-- no translation found for add_contact_dlg_message_fmt (4470846346726847478) -->
|
||||
<skip />
|
||||
<string name="account_setup_basics_title">"設定電子郵件"</string>
|
||||
<string name="account_setup_basics_instructions">"請輸入您帳戶的電子郵件地址:"</string>
|
||||
<string name="account_setup_basics_email_hint">"電子郵件地址"</string>
|
||||
|
|
|
@ -75,9 +75,8 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MessageView extends Activity
|
||||
implements OnClickListener {
|
||||
|
@ -93,6 +92,9 @@ public class MessageView extends Activity
|
|||
};
|
||||
private static final int METHODS_STATUS_COLUMN = 1;
|
||||
|
||||
// regex that matches start of img tag. '.*<(?i)img\s+.*'.
|
||||
private static final Pattern IMG_TAG_START_REGEX = Pattern.compile(".*<(?i)img\\s+.*");
|
||||
|
||||
private TextView mSubjectView;
|
||||
private TextView mFromView;
|
||||
private TextView mDateView;
|
||||
|
@ -451,6 +453,10 @@ public class MessageView extends Activity
|
|||
Intent contactIntent = new Intent(Contacts.Intents.SHOW_OR_CREATE_CONTACT);
|
||||
contactIntent.setData(contactUri);
|
||||
|
||||
// Pass along full E-mail string for possible create dialog
|
||||
contactIntent.putExtra(Contacts.Intents.EXTRA_CREATE_DESCRIPTION,
|
||||
senderEmail.toString());
|
||||
|
||||
// Only provide personal name hint if we have one
|
||||
String senderPersonal = senderEmail.getPersonal();
|
||||
if (senderPersonal != null) {
|
||||
|
@ -694,6 +700,50 @@ public class MessageView extends Activity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve content-id reference in src attribute of img tag to AttachmentProvider's
|
||||
* content uri. This method calls itself recursively at most the number of
|
||||
* LocalAttachmentPart that mime type is image and has content id.
|
||||
* The attribute src="cid:content_id" is resolved as src="content://...".
|
||||
* This method is package scope for testing purpose.
|
||||
*
|
||||
* @param text html email text
|
||||
* @param part mime part which may contain inline image
|
||||
* @return html text in which src attribute of img tag may be replaced with content uri
|
||||
*/
|
||||
/* package */ String resolveInlineImage(String text, Part part, int depth)
|
||||
throws MessagingException {
|
||||
// avoid too deep recursive call.
|
||||
if (depth >= 10) {
|
||||
return text;
|
||||
}
|
||||
String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
|
||||
String contentId = part.getContentId();
|
||||
if (contentType.startsWith("image/") &&
|
||||
contentId != null &&
|
||||
part instanceof LocalAttachmentBodyPart) {
|
||||
LocalAttachmentBodyPart attachment = (LocalAttachmentBodyPart)part;
|
||||
Uri contentUri = AttachmentProvider.getAttachmentUri(
|
||||
mAccount,
|
||||
attachment.getAttachmentId());
|
||||
if (contentUri != null) {
|
||||
// Regexp which matches ' src="cid:contentId"'.
|
||||
String contentIdRe = "\\s+(?i)src=\"cid(?-i):\\Q" + contentId + "\\E\"";
|
||||
// Replace all occurrences of src attribute with ' src="content://contentUri"'.
|
||||
text = text.replaceAll(contentIdRe, " src=\"" + contentUri + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (part.getBody() instanceof Multipart) {
|
||||
Multipart mp = (Multipart)part.getBody();
|
||||
for (int i = 0; i < mp.getCount(); i++) {
|
||||
text = resolveInlineImage(text, mp.getBodyPart(i), depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private void renderAttachments(Part part, int depth) throws MessagingException {
|
||||
String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
|
||||
String name = MimeUtility.getHeaderParameter(contentType, "name");
|
||||
|
@ -875,7 +925,7 @@ public class MessageView extends Activity
|
|||
if (part != null) {
|
||||
String text = MimeUtility.getTextFromPart(part);
|
||||
if (part.getMimeType().equalsIgnoreCase("text/html")) {
|
||||
text = text.replaceAll("cid:", "http://cid/");
|
||||
text = resolveInlineImage(text, mMessage, 0);
|
||||
} else {
|
||||
/*
|
||||
* Linkify the plain text and convert it to HTML by replacing
|
||||
|
@ -898,10 +948,11 @@ public class MessageView extends Activity
|
|||
}
|
||||
|
||||
/*
|
||||
* TODO this should be smarter, change to regex for img, but consider how to
|
||||
* get backgroung images and a million other things that HTML allows.
|
||||
* TODO consider how to get background images and a million other things
|
||||
* that HTML allows.
|
||||
*/
|
||||
if (text.contains("img")) {
|
||||
// Check if text contains img tag.
|
||||
if (IMG_TAG_START_REGEX.matcher(text).matches()) {
|
||||
mHandler.showShowPictures(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,10 +73,10 @@ public class Address {
|
|||
* @return An array of 0 or more Addresses.
|
||||
*/
|
||||
public static Address[] parse(String addressList) {
|
||||
ArrayList<Address> addresses = new ArrayList<Address>();
|
||||
if (addressList == null) {
|
||||
if (addressList == null || addressList.length() == 0) {
|
||||
return new Address[] {};
|
||||
}
|
||||
ArrayList<Address> addresses = new ArrayList<Address>();
|
||||
try {
|
||||
MailboxList parsedList = AddressList.parse(addressList).flatten();
|
||||
for (int i = 0, count = parsedList.size(); i < count; i++) {
|
||||
|
@ -173,7 +173,7 @@ public class Address {
|
|||
* @return
|
||||
*/
|
||||
public static Address[] unpack(String addressList) {
|
||||
if (addressList == null) {
|
||||
if (addressList == null || addressList.length() == 0) {
|
||||
return new Address[] { };
|
||||
}
|
||||
ArrayList<Address> addresses = new ArrayList<Address>();
|
||||
|
@ -205,7 +205,7 @@ public class Address {
|
|||
/**
|
||||
* Packs an address list into a String that is very quick to read
|
||||
* and parse. Packed lists can be unpacked with unpackAddressList()
|
||||
* The packed list is a comma seperated list of:
|
||||
* The packed list is a comma separated list of:
|
||||
* URLENCODE(address)[;URLENCODE(personal)]
|
||||
* @param list
|
||||
* @return
|
||||
|
@ -213,6 +213,8 @@ public class Address {
|
|||
public static String pack(Address[] addresses) {
|
||||
if (addresses == null) {
|
||||
return null;
|
||||
} else if (addresses.length == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0, count = addresses.length; i < count; i++) {
|
||||
|
|
|
@ -31,6 +31,8 @@ public interface Part {
|
|||
public String getContentType() throws MessagingException;
|
||||
|
||||
public String getDisposition() throws MessagingException;
|
||||
|
||||
public String getContentId() throws MessagingException;
|
||||
|
||||
public String[] getHeader(String name) throws MessagingException;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.BufferedWriter;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.android.email.mail.Body;
|
||||
import com.android.email.mail.BodyPart;
|
||||
|
@ -34,6 +35,9 @@ public class MimeBodyPart extends BodyPart {
|
|||
protected Body mBody;
|
||||
protected int mSize;
|
||||
|
||||
// regex that matches content id surrounded by "<>" optionally.
|
||||
private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
|
||||
|
||||
public MimeBodyPart() throws MessagingException {
|
||||
this(null);
|
||||
}
|
||||
|
@ -109,6 +113,16 @@ public class MimeBodyPart extends BodyPart {
|
|||
}
|
||||
}
|
||||
|
||||
public String getContentId() throws MessagingException {
|
||||
String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
|
||||
if (contentId == null) {
|
||||
return null;
|
||||
} else {
|
||||
// remove optionally surrounding brackets.
|
||||
return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
|
||||
}
|
||||
}
|
||||
|
||||
public String getMimeType() throws MessagingException {
|
||||
return MimeUtility.getHeaderParameter(getContentType(), null);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class MimeHeader {
|
|||
public static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||
public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
|
||||
public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
|
||||
public static final String HEADER_CONTENT_ID = "Content-ID";
|
||||
|
||||
/**
|
||||
* Fields that should be omitted when writing the header using writeTo()
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.james.mime4j.BodyDescriptor;
|
||||
import org.apache.james.mime4j.ContentHandler;
|
||||
|
@ -38,6 +39,7 @@ import com.android.email.mail.Body;
|
|||
import com.android.email.mail.BodyPart;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Multipart;
|
||||
import com.android.email.mail.Part;
|
||||
|
||||
/**
|
||||
|
@ -46,12 +48,17 @@ import com.android.email.mail.Part;
|
|||
*/
|
||||
public class MimeMessage extends Message {
|
||||
protected MimeHeader mHeader = new MimeHeader();
|
||||
|
||||
// NOTE: The fields here are transcribed out of headers, and values stored here will supercede
|
||||
// the values found in the headers. Use caution to prevent any out-of-phase errors. In
|
||||
// particular, any adds/changes/deletes here must be echoed by changes in the parse() function.
|
||||
protected Address[] mFrom;
|
||||
protected Address[] mTo;
|
||||
protected Address[] mCc;
|
||||
protected Address[] mBcc;
|
||||
protected Address[] mReplyTo;
|
||||
protected Date mSentDate;
|
||||
|
||||
// In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
|
||||
// "Jan", not the other localized format like "Ene" (meaning January in locale es).
|
||||
// This conversion is used when generating outgoing MIME messages. Incoming MIME date
|
||||
|
@ -62,6 +69,9 @@ public class MimeMessage extends Message {
|
|||
protected Body mBody;
|
||||
protected int mSize;
|
||||
|
||||
// regex that matches content id surrounded by "<>" optionally.
|
||||
private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
|
||||
|
||||
public MimeMessage() {
|
||||
/*
|
||||
* Every new messages gets a Message-ID
|
||||
|
@ -100,12 +110,16 @@ public class MimeMessage extends Message {
|
|||
}
|
||||
|
||||
protected void parse(InputStream in) throws IOException, MessagingException {
|
||||
// Before parsing the input stream, clear all local fields that may be superceded by
|
||||
// the new incoming message.
|
||||
mHeader.clear();
|
||||
mBody = null;
|
||||
mBcc = null;
|
||||
mTo = null;
|
||||
mFrom = null;
|
||||
mTo = null;
|
||||
mCc = null;
|
||||
mBcc = null;
|
||||
mReplyTo = null;
|
||||
mSentDate = null;
|
||||
mBody = null;
|
||||
|
||||
MimeStreamParser parser = new MimeStreamParser();
|
||||
parser.setContentHandler(new MimeMessageBuilder());
|
||||
|
@ -152,6 +166,16 @@ public class MimeMessage extends Message {
|
|||
}
|
||||
}
|
||||
|
||||
public String getContentId() throws MessagingException {
|
||||
String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
|
||||
if (contentId == null) {
|
||||
return null;
|
||||
} else {
|
||||
// remove optionally surrounding brackets.
|
||||
return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
|
||||
}
|
||||
}
|
||||
|
||||
public String getMimeType() throws MessagingException {
|
||||
return MimeUtility.getHeaderParameter(getContentType(), null);
|
||||
}
|
||||
|
|
|
@ -126,13 +126,9 @@ public class MimeUtility {
|
|||
}
|
||||
}
|
||||
}
|
||||
String[] header = part.getHeader("Content-ID");
|
||||
if (header != null) {
|
||||
for (String s : header) {
|
||||
if (s.equals(contentId)) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
String cid = part.getContentId();
|
||||
if (contentId.equals(cid)) {
|
||||
return part;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,15 @@
|
|||
|
||||
package com.android.email.mail.store;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.email.PeekableInputStream;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.transport.LoggingInputStream;
|
||||
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
@ -24,14 +33,6 @@ import java.util.ArrayList;
|
|||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.email.PeekableInputStream;
|
||||
import com.android.email.mail.MessagingException;
|
||||
|
||||
public class ImapResponseParser {
|
||||
// DEBUG ONLY - Always check in as "false"
|
||||
private static boolean DEBUG_LOG_RAW_STREAM = false;
|
||||
|
@ -43,11 +44,11 @@ public class ImapResponseParser {
|
|||
PeekableInputStream mIn;
|
||||
InputStream mActiveLiteral;
|
||||
|
||||
public ImapResponseParser(PeekableInputStream in) {
|
||||
public ImapResponseParser(InputStream in) {
|
||||
if (DEBUG_LOG_RAW_STREAM && Config.LOGD && Email.DEBUG) {
|
||||
in = new LoggingInputStream(in);
|
||||
}
|
||||
this.mIn = in;
|
||||
this.mIn = new PeekableInputStream(in);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -383,69 +384,4 @@ public class ImapResponseParser {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple class used for debugging only that affords us a view of the raw Imap stream,
|
||||
* in addition to the tokenized version.
|
||||
*/
|
||||
private static class LoggingInputStream extends PeekableInputStream {
|
||||
|
||||
PeekableInputStream mIn;
|
||||
StringBuilder mSb;
|
||||
|
||||
public LoggingInputStream(PeekableInputStream in) {
|
||||
super(null);
|
||||
mIn = in;
|
||||
mSb = new StringBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect chars as read, and log them when EOL reached.
|
||||
*/
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int oneByte = mIn.read();
|
||||
logRaw(oneByte);
|
||||
return oneByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect chars as read, and log them when EOL reached.
|
||||
*/
|
||||
@Override
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
int bytesRead = mIn.read(b, offset, length);
|
||||
int copyBytes = bytesRead;
|
||||
while (copyBytes > 0) {
|
||||
logRaw((char)b[offset]);
|
||||
copyBytes--;
|
||||
offset++;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass-through any peeks
|
||||
*/
|
||||
@Override
|
||||
public int peek() throws IOException {
|
||||
return mIn.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write and clear the buffer
|
||||
*/
|
||||
private void logRaw(int oneByte) {
|
||||
if (oneByte == '\r' || oneByte == '\n') {
|
||||
if (mSb.length() > 0) {
|
||||
Log.d(Email.LOG_TAG, "RAW " + mSb.toString());
|
||||
mSb = new StringBuilder();
|
||||
}
|
||||
} else {
|
||||
mSb.append((char)oneByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -856,6 +856,7 @@ public class ImapStore extends Store {
|
|||
if (bs.get(2) instanceof ImapList) {
|
||||
bodyParams = bs.getList(2);
|
||||
}
|
||||
String cid = bs.getString(3);
|
||||
String encoding = bs.getString(5);
|
||||
int size = bs.getNumber(6);
|
||||
|
||||
|
@ -941,6 +942,12 @@ public class ImapStore extends Store {
|
|||
* to parse the body.
|
||||
*/
|
||||
part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
||||
/*
|
||||
* Set the Content-ID header.
|
||||
*/
|
||||
if (!"NIL".equalsIgnoreCase(cid)) {
|
||||
part.setHeader(MimeHeader.HEADER_CONTENT_ID, cid);
|
||||
}
|
||||
|
||||
if (part instanceof ImapMessage) {
|
||||
((ImapMessage) part).setSize(size);
|
||||
|
@ -1084,8 +1091,6 @@ public class ImapStore extends Store {
|
|||
private int mNextCommandTag;
|
||||
|
||||
public void open() throws IOException, MessagingException {
|
||||
PeekableInputStream mIn;
|
||||
|
||||
if (mTransport != null && mTransport.isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1101,8 +1106,7 @@ public class ImapStore extends Store {
|
|||
mTransport.open();
|
||||
mTransport.setSoTimeout(MailTransport.SOCKET_READ_TIMEOUT);
|
||||
|
||||
mIn = new PeekableInputStream(mTransport.getInputStream());
|
||||
mParser = new ImapResponseParser(mIn);
|
||||
mParser = new ImapResponseParser(mTransport.getInputStream());
|
||||
|
||||
// BANNER
|
||||
mParser.readResponse();
|
||||
|
@ -1119,8 +1123,7 @@ public class ImapStore extends Store {
|
|||
|
||||
mTransport.reopenTls();
|
||||
mTransport.setSoTimeout(MailTransport.SOCKET_READ_TIMEOUT);
|
||||
mIn = new PeekableInputStream(mTransport.getInputStream());
|
||||
mParser = new ImapResponseParser(mIn);
|
||||
mParser = new ImapResponseParser(mTransport.getInputStream());
|
||||
} else if (mTransport.getSecurity() ==
|
||||
Transport.CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
if (Config.LOGD && Email.DEBUG) {
|
||||
|
|
|
@ -76,10 +76,11 @@ public class LocalStore extends Store {
|
|||
* ---------- ---------- -----
|
||||
* 18 pre-1.0 Development versions. No upgrade path.
|
||||
* 18 1.0, 1.1 1.0 Release version.
|
||||
* 19 1.5 Added message_id column
|
||||
* 19 - Added message_id column to messages table.
|
||||
* 20 1.5 Added content_id column to attachments table.
|
||||
*/
|
||||
|
||||
private static final int DB_VERSION = 19;
|
||||
private static final int DB_VERSION = 20;
|
||||
|
||||
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN };
|
||||
|
||||
|
@ -110,6 +111,10 @@ public class LocalStore extends Store {
|
|||
}
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(mPath, null);
|
||||
int oldVersion = mDb.getVersion();
|
||||
|
||||
/*
|
||||
* TODO we should have more sophisticated way to upgrade database.
|
||||
*/
|
||||
if (oldVersion != DB_VERSION) {
|
||||
if (Config.LOGV) {
|
||||
Log.v(Email.LOG_TAG, String.format("Upgrading database from %d to %d",
|
||||
|
@ -133,7 +138,7 @@ public class LocalStore extends Store {
|
|||
mDb.execSQL("DROP TABLE IF EXISTS attachments");
|
||||
mDb.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
|
||||
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
|
||||
+ "mime_type TEXT)");
|
||||
+ "mime_type TEXT, content_id TEXT)");
|
||||
|
||||
mDb.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||
mDb.execSQL("CREATE TABLE pending_commands " +
|
||||
|
@ -146,12 +151,21 @@ public class LocalStore extends Store {
|
|||
mDb.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; END;");
|
||||
mDb.setVersion(DB_VERSION);
|
||||
}
|
||||
else if (oldVersion < 19) {
|
||||
/**
|
||||
* Upgrade 18 to 19: add message_id to messages table
|
||||
*/
|
||||
mDb.execSQL("ALTER TABLE messages ADD COLUMN message_id TEXT;");
|
||||
mDb.setVersion(DB_VERSION);
|
||||
else {
|
||||
if (oldVersion < 19) {
|
||||
/**
|
||||
* Upgrade 18 to 19: add message_id to messages table
|
||||
*/
|
||||
mDb.execSQL("ALTER TABLE messages ADD COLUMN message_id TEXT;");
|
||||
mDb.setVersion(19);
|
||||
}
|
||||
if (oldVersion < 20) {
|
||||
/**
|
||||
* Upgrade 19 to 20: add content_id to attachments table
|
||||
*/
|
||||
mDb.execSQL("ALTER TABLE attachments ADD COLUMN content_id TEXT;");
|
||||
mDb.setVersion(20);
|
||||
}
|
||||
}
|
||||
|
||||
if (mDb.getVersion() != DB_VERSION) {
|
||||
|
@ -523,7 +537,8 @@ public class LocalStore extends Store {
|
|||
"name",
|
||||
"mime_type",
|
||||
"store_data",
|
||||
"content_uri" },
|
||||
"content_uri",
|
||||
"content_id" },
|
||||
"message_id = ?",
|
||||
new String[] { Long.toString(localMessage.mId) },
|
||||
null,
|
||||
|
@ -537,6 +552,7 @@ public class LocalStore extends Store {
|
|||
String type = cursor.getString(3);
|
||||
String storeData = cursor.getString(4);
|
||||
String contentUri = cursor.getString(5);
|
||||
String contentId = cursor.getString(6);
|
||||
Body body = null;
|
||||
if (contentUri != null) {
|
||||
body = new LocalAttachmentBody(Uri.parse(contentUri), mContext);
|
||||
|
@ -551,6 +567,7 @@ public class LocalStore extends Store {
|
|||
String.format("attachment;\n filename=\"%s\";\n size=%d",
|
||||
name,
|
||||
size));
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_ID, contentId);
|
||||
|
||||
/*
|
||||
* HEADER_ANDROID_ATTACHMENT_STORE_DATA is a custom header we add to that
|
||||
|
@ -924,6 +941,7 @@ public class LocalStore extends Store {
|
|||
MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA), ',');
|
||||
|
||||
String name = MimeUtility.getHeaderParameter(attachment.getContentType(), "name");
|
||||
String contentId = attachment.getContentId();
|
||||
|
||||
if (attachmentId == -1) {
|
||||
ContentValues cv = new ContentValues();
|
||||
|
@ -933,6 +951,7 @@ public class LocalStore extends Store {
|
|||
cv.put("size", size);
|
||||
cv.put("name", name);
|
||||
cv.put("mime_type", attachment.getMimeType());
|
||||
cv.put("content_id", contentId);
|
||||
|
||||
attachmentId = mDb.insert("attachments", "message_id", cv);
|
||||
}
|
||||
|
@ -940,6 +959,7 @@ public class LocalStore extends Store {
|
|||
ContentValues cv = new ContentValues();
|
||||
cv.put("content_uri", contentUri != null ? contentUri.toString() : null);
|
||||
cv.put("size", size);
|
||||
cv.put("content_id", contentId);
|
||||
mDb.update(
|
||||
"attachments",
|
||||
cv,
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.android.email.mail.Store;
|
|||
import com.android.email.mail.Transport;
|
||||
import com.android.email.mail.Folder.OpenMode;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.transport.LoggingInputStream;
|
||||
import com.android.email.mail.transport.MailTransport;
|
||||
|
||||
import android.util.Config;
|
||||
|
@ -46,6 +47,7 @@ public class Pop3Store extends Store {
|
|||
// All flags defining debug or development code settings must be FALSE
|
||||
// when code is checked in or released.
|
||||
private static boolean DEBUG_FORCE_SINGLE_LINE_UIDL = false;
|
||||
private static boolean DEBUG_LOG_RAW_STREAM = false;
|
||||
|
||||
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED };
|
||||
|
||||
|
@ -718,7 +720,11 @@ public class Pop3Store extends Store {
|
|||
}
|
||||
if (response != null) {
|
||||
try {
|
||||
message.parse(new Pop3ResponseInputStream(mTransport.getInputStream()));
|
||||
InputStream in = mTransport.getInputStream();
|
||||
if (DEBUG_LOG_RAW_STREAM && Config.LOGD && Email.DEBUG) {
|
||||
in = new LoggingInputStream(in);
|
||||
}
|
||||
message.parse(new Pop3ResponseInputStream(in));
|
||||
}
|
||||
catch (MessagingException me) {
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.transport;
|
||||
|
||||
import com.android.email.Email;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Simple class used for debugging only that affords us a view of the raw IMAP or POP3 stream,
|
||||
* in addition to the tokenized version.
|
||||
*
|
||||
* Use of this class *MUST* be restricted to logging-enabled situations only.
|
||||
*/
|
||||
public class LoggingInputStream extends InputStream {
|
||||
|
||||
InputStream mIn;
|
||||
StringBuilder mSb;
|
||||
boolean mBufferDirty;
|
||||
|
||||
private final String LINE_TAG = "RAW ";
|
||||
|
||||
public LoggingInputStream(InputStream in) {
|
||||
super();
|
||||
mIn = in;
|
||||
mSb = new StringBuilder(LINE_TAG);
|
||||
mBufferDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect chars as read, and log them when EOL reached.
|
||||
*/
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int oneByte = mIn.read();
|
||||
logRaw(oneByte);
|
||||
return oneByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect chars as read, and log them when EOL reached.
|
||||
*/
|
||||
@Override
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
int bytesRead = mIn.read(b, offset, length);
|
||||
int copyBytes = bytesRead;
|
||||
while (copyBytes > 0) {
|
||||
logRaw((char)b[offset]);
|
||||
copyBytes--;
|
||||
offset++;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write and clear the buffer
|
||||
*/
|
||||
private void logRaw(int oneByte) {
|
||||
if (oneByte == '\r' || oneByte == '\n') {
|
||||
if (mBufferDirty) {
|
||||
Log.d(Email.LOG_TAG, mSb.toString());
|
||||
mSb = new StringBuilder(LINE_TAG);
|
||||
mBufferDirty = false;
|
||||
}
|
||||
} else {
|
||||
mSb.append((char)oneByte);
|
||||
mBufferDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,16 +21,25 @@ import com.android.email.Email;
|
|||
import com.android.email.MessagingController;
|
||||
import com.android.email.Preferences;
|
||||
import com.android.email.R;
|
||||
import com.android.email.mail.MessageTestUtils;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.MessageTestUtils.MessageBuilder;
|
||||
import com.android.email.mail.MessageTestUtils.MultipartBuilder;
|
||||
import com.android.email.mail.MessageTestUtils.TextBuilder;
|
||||
import com.android.email.mail.internet.BinaryTempFileBody;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.Suppress;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -58,6 +67,7 @@ public class MessageViewTests
|
|||
private TextView mToView;
|
||||
private TextView mSubjectView;
|
||||
private WebView mMessageContentView;
|
||||
private Context mContext;
|
||||
|
||||
public MessageViewTests() {
|
||||
super("com.android.email", MessageView.class);
|
||||
|
@ -66,13 +76,13 @@ public class MessageViewTests
|
|||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Context context = getInstrumentation().getTargetContext();
|
||||
Account[] accounts = Preferences.getPreferences(context).getAccounts();
|
||||
mContext = getInstrumentation().getTargetContext();
|
||||
Account[] accounts = Preferences.getPreferences(mContext).getAccounts();
|
||||
if (accounts.length > 0)
|
||||
{
|
||||
// This depends on getDefaultAccount() to auto-assign the default account, if necessary
|
||||
mAccount = Preferences.getPreferences(context).getDefaultAccount();
|
||||
Email.setServicesEnabled(context);
|
||||
mAccount = Preferences.getPreferences(mContext).getDefaultAccount();
|
||||
Email.setServicesEnabled(mContext);
|
||||
}
|
||||
|
||||
// configure a mock controller
|
||||
|
@ -93,6 +103,9 @@ public class MessageViewTests
|
|||
mToView = (TextView) a.findViewById(R.id.to);
|
||||
mSubjectView = (TextView) a.findViewById(R.id.subject);
|
||||
mMessageContentView = (WebView) a.findViewById(R.id.message_content);
|
||||
|
||||
// This is needed for mime image bodypart.
|
||||
BinaryTempFileBody.setTempDirectory(getActivity().getCacheDir());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,6 +148,90 @@ public class MessageViewTests
|
|||
a.handleMenuItem(R.id.forward);
|
||||
a.handleMenuItem(R.id.mark_as_unread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for resolving inline image src cid: reference to content uri.
|
||||
*/
|
||||
|
||||
public void testResolveInlineImage() throws MessagingException, IOException {
|
||||
final MessageView a = getActivity();
|
||||
final LocalStore store = new LocalStore(mAccount.getLocalStoreUri(), mContext);
|
||||
|
||||
// Single cid case.
|
||||
final String cid1 = "cid.1@android.com";
|
||||
final long aid1 = 10;
|
||||
final Uri uri1 = MessageTestUtils.contentUri(aid1, mAccount);
|
||||
final String text1 = new TextBuilder("text1 > ").addCidImg(cid1).build(" <.");
|
||||
final String expected1 = new TextBuilder("text1 > ").addUidImg(uri1).build(" <.");
|
||||
|
||||
// message with cid1
|
||||
final Message msg1 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", "<"+cid1+">", aid1, store))
|
||||
.build())
|
||||
.build();
|
||||
// Simple case.
|
||||
final String actual1 = a.resolveInlineImage(text1, msg1, 0);
|
||||
assertEquals("one content id reference is not resolved",
|
||||
expected1, actual1);
|
||||
|
||||
// Exceed recursive limit.
|
||||
final String actual0 = a.resolveInlineImage(text1, msg1, 10);
|
||||
assertEquals("recursive call limit may exceeded",
|
||||
text1, actual0);
|
||||
|
||||
// Multiple cids case.
|
||||
final String cid2 = "cid.2@android.com";
|
||||
final long aid2 = 20;
|
||||
final Uri uri2 = MessageTestUtils.contentUri(aid2, mAccount);
|
||||
final String text2 = new TextBuilder("text2 ").addCidImg(cid2).build(".");
|
||||
final String expected2 = new TextBuilder("text2 ").addUidImg(uri2).build(".");
|
||||
|
||||
// message with only cid2
|
||||
final Message msg2 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text1 + text2))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 is not replaced
|
||||
final String actual2 = a.resolveInlineImage(text1 + text2, msg2, 0);
|
||||
assertEquals("only one of two content id is resolved",
|
||||
text1 + expected2, actual2);
|
||||
|
||||
// message with cid1 and cid2
|
||||
final Message msg3 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text2 + text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", cid1, aid1, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 and cid2 are replaced
|
||||
final String actual3 = a.resolveInlineImage(text2 + text1, msg3, 0);
|
||||
assertEquals("two content ids are resolved correctly",
|
||||
expected2 + expected1, actual3);
|
||||
|
||||
// message with many cids and normal attachments
|
||||
final Message msg4 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/mixed")
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", null, 30, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("application/pdf", cid1, aid1, store))
|
||||
.addBodyPart(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text2 + text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpg", cid1, aid1, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.buildBodyPart())
|
||||
.addBodyPart(MessageTestUtils.imagePart("application/pdf", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 and cid2 are replaced
|
||||
final String actual4 = a.resolveInlineImage(text2 + text1, msg4, 0);
|
||||
assertEquals("two content ids in deep multipart level are resolved",
|
||||
expected2 + expected1, actual4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mock Messaging controller, so we can drive its callbacks. This probably should be
|
||||
|
|
|
@ -43,9 +43,27 @@ public class AddressUnitTests extends AndroidTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO: test parse()
|
||||
* TODO: more in-depth tests for parse()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple quick checks of empty-input edge conditions for parse()
|
||||
*
|
||||
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
|
||||
* behavior while I am changing some of the code in the function under test.
|
||||
*/
|
||||
public void testEmptyParse() {
|
||||
Address[] result;
|
||||
|
||||
// null input => empty array
|
||||
result = Address.parse(null);
|
||||
assertTrue("parsing null address", result != null && result.length == 0);
|
||||
|
||||
// empty string input => empty array
|
||||
result = Address.parse("");
|
||||
assertTrue("parsing zero-length", result != null && result.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: test toString() (single & list)
|
||||
*/
|
||||
|
@ -75,6 +93,43 @@ public class AddressUnitTests extends AndroidTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO: test pack() and unpack()
|
||||
* TODO: more in-depth tests for pack() and unpack()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple quick checks of empty-input edge conditions for pack()
|
||||
*
|
||||
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
|
||||
* behavior while I am changing some of the code in the function under test.
|
||||
*/
|
||||
public void testEmptyPack() {
|
||||
String result;
|
||||
|
||||
// null input => null string
|
||||
result = Address.pack(null);
|
||||
assertNull("packing null", result);
|
||||
|
||||
// zero-length input => empty string
|
||||
result = Address.pack(new Address[] { });
|
||||
assertEquals("packing empty array", "", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple quick checks of empty-input edge conditions for unpack()
|
||||
*
|
||||
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
|
||||
* behavior while I am changing some of the code in the function under test.
|
||||
*/
|
||||
public void testEmptyUnpack() {
|
||||
Address[] result;
|
||||
|
||||
// null input => empty array
|
||||
result = Address.unpack(null);
|
||||
assertTrue("unpacking null address", result != null && result.length == 0);
|
||||
|
||||
// empty string input => empty array
|
||||
result = Address.unpack("");
|
||||
assertTrue("unpacking zero-length", result != null && result.length == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail;
|
||||
|
||||
import com.android.email.Account;
|
||||
import com.android.email.mail.internet.BinaryTempFileBody;
|
||||
import com.android.email.mail.internet.MimeBodyPart;
|
||||
import com.android.email.mail.internet.MimeHeader;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.internet.MimeMultipart;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.AttachmentProvider;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility class makes it easier for developer to build mail message objects.
|
||||
* <p>
|
||||
* Typical usage of these helper functions and builder objects are as follows.
|
||||
* <p>
|
||||
* <pre>
|
||||
* String text2 = new TextBuilder("<html>").text("<head></head>")
|
||||
* .text("<body>").cidImg("contetid@domain").text("</body>").build("</html");
|
||||
* String text2 = new TextBuilder("<html>").text("<head></head>")
|
||||
* .text("<body>").uriImg(contentUri).text("</body>").build("</html");
|
||||
* Message msg = new MessageBuilder()
|
||||
* .setBody(new MultipartBuilder("multipart/mixed")
|
||||
* .addBodyPart(MessageTestUtils.imagePart("image/jpeg", null, 30, store))
|
||||
* .addBodyPart(MessageTestUtils.imagePart("application/pdf", cid1, aid1, store))
|
||||
* .addBodyPart(new MultipartBuilder("multipart/related")
|
||||
* .addBodyPart(MessageTestUtils.textPart("text/html", text2 + text1))
|
||||
* .addBodyPart(MessageTestUtils.imagePart("image/jpg", cid1, aid1, store))
|
||||
* .addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
* .buildBodyPart())
|
||||
* .addBodyPart(MessageTestUtils.imagePart("application/pdf", cid2, aid2, store))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class MessageTestUtils {
|
||||
|
||||
/**
|
||||
* Generate AttachmentProvider content URI from attachment ID and Account.
|
||||
*
|
||||
* @param attachmentId attachment id
|
||||
* @param account Account object
|
||||
* @return AttachmentProvider content URI
|
||||
*/
|
||||
public static Uri contentUri(long attachmentId, Account account) {
|
||||
return AttachmentProvider.getAttachmentUri(account, attachmentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create simple MimeBodyPart.
|
||||
*
|
||||
* @param mimeType MIME type of body part
|
||||
* @param contentId content-id header value (optional - null for no header)
|
||||
* @return MimeBodyPart object which body is null.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public static BodyPart bodyPart(String mimeType, String contentId) throws MessagingException {
|
||||
final MimeBodyPart bp = new MimeBodyPart(null, mimeType);
|
||||
if (contentId != null) {
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_ID, contentId);
|
||||
}
|
||||
return bp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create MimeBodyPart with TextBody.
|
||||
*
|
||||
* @param mimeType MIME type of text
|
||||
* @param text body text string
|
||||
* @return MimeBodyPart object whose body is TextBody
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public static BodyPart textPart(String mimeType, String text) throws MessagingException {
|
||||
final TextBody textBody = new TextBody(text);
|
||||
final MimeBodyPart textPart = new MimeBodyPart(textBody);
|
||||
textPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
|
||||
return textPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attachment BodyPart with content-id.
|
||||
*
|
||||
* @param mimeType MIME type of image body
|
||||
* @param contentId content-id header value (optional - null for no header)
|
||||
* @param attachmentId attachment id of store
|
||||
* @param store LocalStore which stores attachment
|
||||
* @return LocalAttachmentBodyPart with content-id
|
||||
* @throws MessagingException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BodyPart imagePart(String mimeType, String contentId,
|
||||
long attachmentId, LocalStore store) throws MessagingException, IOException {
|
||||
final BinaryTempFileBody imageBody = new BinaryTempFileBody();
|
||||
final LocalStore.LocalAttachmentBodyPart imagePart =
|
||||
store.new LocalAttachmentBodyPart(imageBody, attachmentId);
|
||||
imagePart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
|
||||
if (contentId != null) {
|
||||
imagePart.setHeader(MimeHeader.HEADER_CONTENT_ID, contentId);
|
||||
}
|
||||
return imagePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for Multipart.
|
||||
*
|
||||
* This builder object accepts any number of BodyParts and then can produce
|
||||
* Multipart or BodyPart which contains accepted BodyParts. Usually combined with other
|
||||
* builder object and helper method.
|
||||
*/
|
||||
public static class MultipartBuilder {
|
||||
private final String mContentType;
|
||||
private final ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
|
||||
|
||||
/**
|
||||
* Create builder object with MIME type and dummy boundary string.
|
||||
*
|
||||
* @param mimeType MIME type of this Multipart
|
||||
*/
|
||||
public MultipartBuilder(String mimeType) {
|
||||
this(mimeType, "this_is_boundary");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create builder object with MIME type and boundary string.
|
||||
*
|
||||
* @param mimeType MIME type of this Multipart
|
||||
* @param boundary boundary string
|
||||
*/
|
||||
public MultipartBuilder(String mimeType, String boundary) {
|
||||
mContentType = mimeType + "; boundary=" + boundary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to add BodyPart to intended Multipart.
|
||||
*
|
||||
* @param bodyPart BodyPart to be added
|
||||
* @return builder object itself
|
||||
*/
|
||||
public MultipartBuilder addBodyPart(final BodyPart bodyPart) {
|
||||
mParts.add(bodyPart);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build method to create Multipart.
|
||||
*
|
||||
* @return intended Multipart object
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public Multipart build() throws MessagingException {
|
||||
final MimeMultipart mp = new MimeMultipart(mContentType);
|
||||
for (BodyPart p : mParts) {
|
||||
mp.addBodyPart(p);
|
||||
}
|
||||
return mp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build method to create BodyPart that contains this "Multipart"
|
||||
* @return BodyPart whose body is intended Multipart.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public BodyPart buildBodyPart() throws MessagingException {
|
||||
final BodyPart bp = new MimeBodyPart();
|
||||
bp.setBody(this.build());
|
||||
return bp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for Message
|
||||
*
|
||||
* This builder object accepts Body and then can produce Message object.
|
||||
* Usually combined with other builder object and helper method.
|
||||
*/
|
||||
public static class MessageBuilder {
|
||||
private Body mBody;
|
||||
|
||||
/**
|
||||
* Create Builder object.
|
||||
*/
|
||||
public MessageBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to set Body.
|
||||
*
|
||||
* @param body Body of intended Message
|
||||
* @return builder object itself
|
||||
*/
|
||||
public MessageBuilder setBody(final Body body) {
|
||||
mBody = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build method to create Message.
|
||||
*
|
||||
* @return intended Message object
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public Message build() throws MessagingException {
|
||||
final MimeMessage msg = new MimeMessage();
|
||||
if (mBody == null) {
|
||||
throw new MessagingException("body is not specified");
|
||||
}
|
||||
msg.setBody(mBody);
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for simple HTML String.
|
||||
* This builder object accepts some type of object or and string and then create String object.
|
||||
* Usually combined with other builder object and helper method.
|
||||
*/
|
||||
public static class TextBuilder {
|
||||
final StringBuilder mBuilder = new StringBuilder();
|
||||
|
||||
/**
|
||||
* Create builder with preamble string
|
||||
* @param preamble
|
||||
*/
|
||||
public TextBuilder(String preamble) {
|
||||
mBuilder.append(preamble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to add img tag that has cid: src attribute.
|
||||
* @param contentId content id string
|
||||
* @return builder object itself
|
||||
*/
|
||||
public TextBuilder addCidImg(String contentId) {
|
||||
return addTag("img", "SRC", "cid:" + contentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to add img tag that has content:// src attribute.
|
||||
* @param contentUri content uri object
|
||||
* @return builder object itself
|
||||
*/
|
||||
public TextBuilder addUidImg(Uri contentUri) {
|
||||
return addTag("img", "src", contentUri.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to add tag with specified attribute and value.
|
||||
*
|
||||
* @param tag tag name
|
||||
* @param attribute attribute name
|
||||
* @param value attribute value
|
||||
* @return builder object itself
|
||||
*/
|
||||
public TextBuilder addTag(String tag, String attribute, String value) {
|
||||
return addText(String.format("<%s %s=\"%s\">", tag, attribute, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier method to add simple string.
|
||||
* @param text string to add
|
||||
* @return builder object itself
|
||||
*/
|
||||
public TextBuilder addText(String text) {
|
||||
mBuilder.append(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build method to create intended String
|
||||
* @param epilogue string to add to the end
|
||||
* @return intended String
|
||||
*/
|
||||
public String build(String epilogue) {
|
||||
mBuilder.append(epilogue);
|
||||
return mBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.internet;
|
||||
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.internet.MimeBodyPart;
|
||||
import com.android.email.mail.internet.MimeHeader;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
/**
|
||||
* This is a series of unit tests for the MimeBodyPart class. These tests must be locally
|
||||
* complete - no server(s) required.
|
||||
*/
|
||||
@SmallTest
|
||||
public class MimeBodyPartTest extends TestCase {
|
||||
|
||||
// TODO: more tests.
|
||||
|
||||
/*
|
||||
* Confirm getContentID() correctly works.
|
||||
*/
|
||||
public void testGetContentId() throws MessagingException {
|
||||
MimeBodyPart bp = new MimeBodyPart();
|
||||
|
||||
// no content-id
|
||||
assertNull(bp.getContentId());
|
||||
|
||||
// normal case
|
||||
final String cid1 = "cid.1@android.com";
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_ID, cid1);
|
||||
assertEquals(cid1, bp.getContentId());
|
||||
|
||||
// surrounded by optional bracket
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_ID, "<" + cid1 + ">");
|
||||
assertEquals(cid1, bp.getContentId());
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
package com.android.email.mail.internet;
|
||||
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.internet.MimeHeader;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
|
@ -95,4 +97,23 @@ public class MimeMessageTest extends TestCase {
|
|||
message2.setMessageId(testId2);
|
||||
assertEquals("set and get Message-ID", testId2, message2.getMessageId());
|
||||
}
|
||||
|
||||
/*
|
||||
* Confirm getContentID() correctly works.
|
||||
*/
|
||||
public void testGetContentId() throws MessagingException {
|
||||
MimeMessage message = new MimeMessage();
|
||||
|
||||
// no content-id
|
||||
assertNull(message.getContentId());
|
||||
|
||||
// normal case
|
||||
final String cid1 = "cid.1@android.com";
|
||||
message.setHeader(MimeHeader.HEADER_CONTENT_ID, cid1);
|
||||
assertEquals(cid1, message.getContentId());
|
||||
|
||||
// surrounded by optional bracket
|
||||
message.setHeader(MimeHeader.HEADER_CONTENT_ID, "<" + cid1 + ">");
|
||||
assertEquals(cid1, message.getContentId());
|
||||
}
|
||||
}
|
|
@ -16,7 +16,13 @@
|
|||
|
||||
package com.android.email.mail.internet;
|
||||
|
||||
import com.android.email.mail.BodyPart;
|
||||
import com.android.email.mail.MessageTestUtils;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Part;
|
||||
import com.android.email.mail.MessageTestUtils.MessageBuilder;
|
||||
import com.android.email.mail.MessageTestUtils.MultipartBuilder;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
|
@ -35,7 +41,46 @@ public class MimeUtilityTest extends TestCase {
|
|||
// TODO: tests for foldAndEncode(String s)
|
||||
// TODO: tests for getHeaderParameter(String header, String name)
|
||||
// TODO: tests for findFirstPartByMimeType(Part part, String mimeType)
|
||||
// TODO: tests for findPartByContentId(Part part, String contentId) throws Exception
|
||||
|
||||
/** Tests for findPartByContentId(Part part, String contentId) */
|
||||
public void testFindPartByContentIdTestCase() throws MessagingException, Exception {
|
||||
final String cid1 = "cid.1@android.com";
|
||||
final Part cid1bp = MessageTestUtils.bodyPart("image/gif", cid1);
|
||||
final String cid2 = "cid.2@android.com";
|
||||
final Part cid2bp = MessageTestUtils.bodyPart("image/gif", "<" + cid2 + ">");
|
||||
|
||||
final Message msg1 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.bodyPart("text/html", null))
|
||||
.addBodyPart((BodyPart)cid1bp)
|
||||
.build())
|
||||
.build();
|
||||
// found cid1 part
|
||||
final Part actual1_1 = MimeUtility.findPartByContentId(msg1, cid1);
|
||||
assertEquals("could not found expected content-id part", cid1bp, actual1_1);
|
||||
|
||||
final Message msg2 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/mixed")
|
||||
.addBodyPart(MessageTestUtils.bodyPart("image/tiff", "cid.4@android.com"))
|
||||
.addBodyPart(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(new MultipartBuilder("multipart/alternative")
|
||||
.addBodyPart(MessageTestUtils.bodyPart("text/plain", null))
|
||||
.addBodyPart(MessageTestUtils.bodyPart("text/html", null))
|
||||
.buildBodyPart())
|
||||
.addBodyPart((BodyPart)cid1bp)
|
||||
.buildBodyPart())
|
||||
.addBodyPart(MessageTestUtils.bodyPart("image/gif", "cid.3@android.com"))
|
||||
.addBodyPart((BodyPart)cid2bp)
|
||||
.build())
|
||||
.build();
|
||||
// found cid1 part
|
||||
final Part actual2_1 = MimeUtility.findPartByContentId(msg2, cid1);
|
||||
assertEquals("found part from related multipart", cid1bp, actual2_1);
|
||||
|
||||
// found cid2 part
|
||||
final Part actual2_2 = MimeUtility.findPartByContentId(msg2, cid2);
|
||||
assertEquals("found part from mixed multipart", cid2bp, actual2_2);
|
||||
}
|
||||
|
||||
/** Tests for getTextFromPart(Part part) */
|
||||
public void testGetTextFromPartContentTypeCase() throws MessagingException {
|
||||
|
@ -111,4 +156,5 @@ public class MimeUtilityTest extends TestCase {
|
|||
|
||||
// TODO: tests for decodeBody(InputStream in, String contentTransferEncoding)
|
||||
// TODO: tests for collectParts(Part part, ArrayList<Part> viewables, ArrayList<Part> attachments)
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package com.android.email.mail.store;
|
|||
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessageRetrievalListener;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Folder.OpenMode;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
|
@ -26,13 +25,15 @@ import com.android.email.mail.internet.BinaryTempFileBody;
|
|||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockApplication;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* This is a series of unit tests for the LocalStore class.
|
||||
|
@ -53,6 +54,7 @@ public class LocalStoreUnitTests extends AndroidTestCase {
|
|||
private String mLocalStoreUri = null;
|
||||
private LocalStore mStore = null;
|
||||
private LocalStore.LocalFolder mFolder = null;
|
||||
private File mCacheDir;
|
||||
|
||||
/**
|
||||
* Setup code. We generate a lightweight LocalStore and LocalStore.LocalFolder.
|
||||
|
@ -69,7 +71,8 @@ public class LocalStoreUnitTests extends AndroidTestCase {
|
|||
mFolder = (LocalStore.LocalFolder) mStore.getFolder("TEST");
|
||||
|
||||
// This is needed for parsing mime messages
|
||||
BinaryTempFileBody.setTempDirectory(this.getContext().getCacheDir());
|
||||
mCacheDir = getContext().getCacheDir();
|
||||
BinaryTempFileBody.setTempDirectory(mCacheDir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,6 +202,200 @@ public class LocalStoreUnitTests extends AndroidTestCase {
|
|||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for database version.
|
||||
*/
|
||||
public void testDbVersion() throws MessagingException, URISyntaxException {
|
||||
final LocalStore store = new LocalStore(mLocalStoreUri, getContext());
|
||||
final URI uri = new URI(mLocalStoreUri);
|
||||
final String dbPath = uri.getPath();
|
||||
final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
|
||||
|
||||
// database version should be latest.
|
||||
assertEquals("database version should be latest", 20, db.getVersion());
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function convert Cursor data to ContentValues
|
||||
*/
|
||||
private ContentValues cursorToContentValues(Cursor c, String[] schema) {
|
||||
if (c.getColumnCount() != schema.length) {
|
||||
throw new IndexOutOfBoundsException("schema length is not mach with cursor columns");
|
||||
}
|
||||
|
||||
final ContentValues cv = new ContentValues();
|
||||
for (int i = 0, count = c.getColumnCount(); i < count; ++i) {
|
||||
final String key = c.getColumnName(i);
|
||||
final String type = schema[i];
|
||||
if (type == "text") {
|
||||
cv.put(key, c.getString(i));
|
||||
} else if (type == "integer" || type == "primary") {
|
||||
cv.put(key, c.getLong(i));
|
||||
} else if (type == "numeric" || type == "real") {
|
||||
cv.put(key, c.getDouble(i));
|
||||
} else if (type == "blob") {
|
||||
cv.put(key, c.getBlob(i));
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported type at index " + i);
|
||||
}
|
||||
}
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for database upgrade from version 18 to version 20.
|
||||
*/
|
||||
public void testDbUpgrade18To20() throws MessagingException, URISyntaxException {
|
||||
final URI uri = new URI(mLocalStoreUri);
|
||||
final String dbPath = uri.getPath();
|
||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
|
||||
|
||||
// create sample version 18 db tables
|
||||
createSampleDb(db, 18);
|
||||
|
||||
// sample message data and expected data
|
||||
final ContentValues initialMessage = new ContentValues();
|
||||
initialMessage.put("folder_id", (long) 2); // folder_id type integer == Long
|
||||
initialMessage.put("internal_date", (long) 3); // internal_date type integer == Long
|
||||
final ContentValues expectedMessage = new ContentValues(initialMessage);
|
||||
expectedMessage.put("id", db.insert("messages", null, initialMessage));
|
||||
|
||||
// sample attachment data and expected data
|
||||
final ContentValues initialAttachment = new ContentValues();
|
||||
initialAttachment.put("message_id", (long) 4); // message_id type integer == Long
|
||||
initialAttachment.put("mime_type", (String) "a"); // mime_type type text == String
|
||||
final ContentValues expectedAttachment = new ContentValues(initialAttachment);
|
||||
expectedAttachment.put("id", db.insert("attachments", null, initialAttachment));
|
||||
db.close();
|
||||
|
||||
// upgrade database 18 to 20
|
||||
new LocalStore(mLocalStoreUri, getContext());
|
||||
|
||||
// added message_id column should be initialized as null
|
||||
expectedMessage.put("message_id", (String) null); // message_id type text == String
|
||||
// added content_id column should be initialized as null
|
||||
expectedAttachment.put("content_id", (String) null); // content_id type text == String
|
||||
|
||||
// database should be upgraded
|
||||
db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
|
||||
assertEquals("database should be upgraded", 20, db.getVersion());
|
||||
Cursor c;
|
||||
|
||||
// check message table
|
||||
c = db.query("messages",
|
||||
new String[] { "id", "folder_id", "internal_date", "message_id" },
|
||||
null, null, null, null, null);
|
||||
// check if data is available
|
||||
assertTrue("messages table should have one data", c.moveToNext());
|
||||
|
||||
// check if data are expected
|
||||
final ContentValues actualMessage = cursorToContentValues(c,
|
||||
new String[] { "primary", "integer", "integer", "text" });
|
||||
assertEquals("messages table cursor does not have expected values",
|
||||
expectedMessage, actualMessage);
|
||||
c.close();
|
||||
|
||||
// check attachment table
|
||||
c = db.query("attachments",
|
||||
new String[] { "id", "message_id", "mime_type", "content_id" },
|
||||
null, null, null, null, null);
|
||||
// check if data is available
|
||||
assertTrue("attachments table should have one data", c.moveToNext());
|
||||
|
||||
// check if data are expected
|
||||
final ContentValues actualAttachment = cursorToContentValues(c,
|
||||
new String[] { "primary", "integer", "text", "text" });
|
||||
assertEquals("attachment table cursor does not have expected values",
|
||||
expectedAttachment, actualAttachment);
|
||||
c.close();
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for database upgrade from version 19 to version 20.
|
||||
*/
|
||||
public void testDbUpgrade19To20() throws MessagingException, URISyntaxException {
|
||||
final URI uri = new URI(mLocalStoreUri);
|
||||
final String dbPath = uri.getPath();
|
||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
|
||||
|
||||
// create minimu version 18 db tables
|
||||
createSampleDb(db, 19);
|
||||
|
||||
// sample message data and expected data
|
||||
final ContentValues initialMessage = new ContentValues();
|
||||
initialMessage.put("folder_id", (long) 2); // folder_id type integer == Long
|
||||
initialMessage.put("internal_date", (long) 3); // internal_date integer == Long
|
||||
initialMessage.put("message_id", (String) "x"); // message_id text == String
|
||||
final ContentValues expectedMessage = new ContentValues(initialMessage);
|
||||
expectedMessage.put("id", db.insert("messages", null, initialMessage));
|
||||
|
||||
// sample attachment data and expected data
|
||||
final ContentValues initialAttachment = new ContentValues();
|
||||
initialAttachment.put("message_id", (long) 4); // message_id type integer == Long
|
||||
initialAttachment.put("mime_type", (String) "a"); // mime_type type text == String
|
||||
final ContentValues expectedAttachment = new ContentValues(initialAttachment);
|
||||
expectedAttachment.put("id", db.insert("attachments", null, initialAttachment));
|
||||
|
||||
db.close();
|
||||
|
||||
// upgrade database 19 to 20
|
||||
new LocalStore(mLocalStoreUri, getContext());
|
||||
|
||||
// added content_id column should be initialized as null
|
||||
expectedAttachment.put("content_id", (String) null); // content_id type text == String
|
||||
|
||||
// database should be upgraded
|
||||
db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
|
||||
assertEquals(20, db.getVersion());
|
||||
Cursor c;
|
||||
|
||||
// check message table
|
||||
c = db.query("messages",
|
||||
new String[] { "id", "folder_id", "internal_date", "message_id" },
|
||||
null, null, null, null, null);
|
||||
// check if data is available
|
||||
assertTrue("attachments table should have one data", c.moveToNext());
|
||||
|
||||
// check if data are expected
|
||||
final ContentValues actualMessage = cursorToContentValues(c,
|
||||
new String[] { "primary", "integer", "integer", "text" });
|
||||
assertEquals("messages table cursor does not have expected values",
|
||||
expectedMessage, actualMessage);
|
||||
|
||||
// check attachment table
|
||||
c = db.query("attachments",
|
||||
new String[] { "id", "message_id", "mime_type", "content_id" },
|
||||
null, null, null, null, null);
|
||||
// check if data is available
|
||||
assertTrue("attachments table should have one data", c.moveToNext());
|
||||
|
||||
// check if data are expected
|
||||
final ContentValues actualAttachment = cursorToContentValues(c,
|
||||
new String[] { "primary", "integer", "text", "text" });
|
||||
assertEquals("attachment table cursor does not have expected values",
|
||||
expectedAttachment, actualAttachment);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
private static void createSampleDb(SQLiteDatabase db, int version) {
|
||||
db.execSQL("DROP TABLE IF EXISTS messages");
|
||||
db.execSQL("CREATE TABLE messages (id INTEGER PRIMARY KEY, folder_id INTEGER, " +
|
||||
"uid TEXT, subject TEXT, date INTEGER, flags TEXT, sender_list TEXT, " +
|
||||
"to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, " +
|
||||
"html_content TEXT, text_content TEXT, attachment_count INTEGER, " +
|
||||
"internal_date INTEGER" +
|
||||
((version >= 19) ? ", message_id TEXT" : "") +
|
||||
")");
|
||||
db.execSQL("DROP TABLE IF EXISTS attachments");
|
||||
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER," +
|
||||
"store_data TEXT, content_uri TEXT, size INTEGER, name TEXT," +
|
||||
"mime_type TEXT" +
|
||||
((version >= 20) ? ", content_id" : "") +
|
||||
")");
|
||||
db.setVersion(version);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.android.email.mail.Folder.FolderType;
|
|||
import com.android.email.mail.Folder.OpenMode;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
import com.android.email.mail.internet.BinaryTempFileBody;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.transport.MockTransport;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
@ -529,6 +530,16 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
|
|||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
mFolder.fetch(messages, fp, null);
|
||||
assertEquals(PER_MESSAGE_SIZE, messages[0].getSize());
|
||||
|
||||
// A side effect of how messages work is that if you get fields that are empty,
|
||||
// then empty arrays are written back into the parsed header fields (e.g. mTo, mFrom). The
|
||||
// standard message parser needs to clear these before parsing. Make sure that this
|
||||
// is happening. (This doesn't affect IMAP, which reads the headers directly via
|
||||
// IMAP evelopes.)
|
||||
MimeMessage message = (MimeMessage) messages[0];
|
||||
message.getRecipients(RecipientType.TO);
|
||||
message.getRecipients(RecipientType.CC);
|
||||
message.getRecipients(RecipientType.BCC);
|
||||
|
||||
// now try fetching the message
|
||||
setupSingleMessage(mockTransport, 1, false);
|
||||
|
@ -586,6 +597,20 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
|
|||
assertEquals(1, from.length);
|
||||
assertEquals("Jones@Registry.Org", from[0].getAddress());
|
||||
assertNull(from[0].getPersonal());
|
||||
|
||||
// check Cc:
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
assertNotNull(cc);
|
||||
assertEquals(1, cc.length);
|
||||
assertEquals("Chris@Registry.Org", cc[0].getAddress());
|
||||
assertNull(cc[0].getPersonal());
|
||||
|
||||
// check Reply-To:
|
||||
Address[] replyto = message.getReplyTo();
|
||||
assertNotNull(replyto);
|
||||
assertEquals(1, replyto.length);
|
||||
assertEquals("Roger@Registry.Org", replyto[0].getAddress());
|
||||
assertNull(replyto[0].getPersonal());
|
||||
|
||||
// TODO date
|
||||
|
||||
|
@ -649,6 +674,10 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
|
|||
* Date: 26 Aug 76 1429 EDT
|
||||
* From: Jones@Registry.Org
|
||||
* To: Smith@Registry.Org
|
||||
*
|
||||
* We'll add the following fields to support additional tests:
|
||||
* Cc: Chris@Registry.Org
|
||||
* Reply-To: Roger@Registry.Org
|
||||
*
|
||||
* @param transport the mock transport to preload
|
||||
* @param msgNum the message number to expect and return
|
||||
|
@ -659,6 +688,8 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
|
|||
transport.expect(null, "Date: 26 Aug 76 1429 EDT");
|
||||
transport.expect(null, "From: Jones@Registry.Org");
|
||||
transport.expect(null, "To: Smith@Registry.Org");
|
||||
transport.expect(null, "CC: Chris@Registry.Org");
|
||||
transport.expect(null, "Reply-To: Roger@Registry.Org");
|
||||
transport.expect(null, "");
|
||||
transport.expect(null, ".");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue