Email split, part trois: AccountService

* Create AccountService.aidl and AccountServiceProxy in emailcommon
* Implement AccountService in email
* Use AccountServiceProxy in Exchange for account reconciliation,
  notifications, etc.
* Move sync window constants into emailcommon
* Split attachment provider utilities and constants into emailcommon

Bug: 3442973
Change-Id: I89dce28b799b193243c07774dab65d830ae62775
This commit is contained in:
Marc Blank 2011-02-08 17:50:30 -08:00
parent e86789b66e
commit 8a57469460
30 changed files with 2005 additions and 1452 deletions

View File

@ -21,7 +21,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += \
src/com/android/emailcommon/service/IEmailService.aidl \
src/com/android/emailcommon/service/IEmailServiceCallback.aidl \
src/com/android/emailcommon/service/IPolicyService.aidl
src/com/android/emailcommon/service/IPolicyService.aidl \
src/com/android/emailcommon/service/IAccountService.aidl
LOCAL_STATIC_JAVA_LIBRARIES := android-common
# Revive this when the app is unbundled.

View File

@ -370,6 +370,17 @@
</intent-filter>
</service>
<service
android:name=".service.AccountService"
android:enabled="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
>
<intent-filter>
<action
android:name="com.android.email.ACCOUNT_INTENT" />
</intent-filter>
</service>
<!--EXCHANGE-REMOVE-SECTION-START-->
<!--Required stanza to register the EAS EmailSyncAdapterService with SyncManager -->
<service

View File

@ -17,6 +17,7 @@
package com.android.email;
import com.android.email.mail.Store;
import com.android.emailcommon.service.SyncWindow;
import android.content.Context;
import android.content.SharedPreferences;
@ -37,14 +38,6 @@ public class Account {
public static final int CHECK_INTERVAL_NEVER = -1;
public static final int CHECK_INTERVAL_PUSH = -2;
public static final int SYNC_WINDOW_USER = -1;
public static final int SYNC_WINDOW_1_DAY = 1;
public static final int SYNC_WINDOW_3_DAYS = 2;
public static final int SYNC_WINDOW_1_WEEK = 3;
public static final int SYNC_WINDOW_2_WEEKS = 4;
public static final int SYNC_WINDOW_1_MONTH = 5;
public static final int SYNC_WINDOW_ALL = 6;
// These flags will never be seen in a "real" (legacy) account
public static final int BACKUP_FLAGS_IS_BACKUP = 1;
public static final int BACKUP_FLAGS_SYNC_CONTACTS = 2;
@ -109,7 +102,7 @@ public class Account {
mVibrate = false;
mVibrateWhenSilent = false;
mRingtoneUri = "content://settings/system/notification_sound";
mSyncWindow = SYNC_WINDOW_USER; // IMAP & POP3
mSyncWindow = SyncWindow.SYNC_WINDOW_USER; // IMAP & POP3
mBackupFlags = 0;
mProtocolVersion = null;
mSecurityFlags = 0;
@ -171,7 +164,7 @@ public class Account {
"content://settings/system/notification_sound");
mSyncWindow = preferences.mSharedPreferences.getInt(mUuid + KEY_SYNC_WINDOW,
SYNC_WINDOW_USER);
SyncWindow.SYNC_WINDOW_USER);
mBackupFlags = preferences.mSharedPreferences.getInt(mUuid + KEY_BACKUP_FLAGS, 0);
mProtocolVersion = preferences.mSharedPreferences.getString(mUuid + KEY_PROTOCOL_VERSION,

View File

@ -17,8 +17,8 @@
package com.android.email;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent.Attachment;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.content.Context;
import android.content.Intent;
@ -69,7 +69,7 @@ public class AttachmentInfo {
public AttachmentInfo(Context context, long id, long size, String fileName, String mimeType,
long accountKey) {
mSize = size;
mContentType = AttachmentProvider.inferMimeType(fileName, mimeType);
mContentType = AttachmentUtilities.inferMimeType(fileName, mimeType);
mName = fileName;
mId = id;
mAccountKey = accountKey;
@ -83,7 +83,7 @@ public class AttachmentInfo {
}
// Check for unacceptable attachments by filename extension
String extension = AttachmentProvider.getFilenameExtension(mName);
String extension = AttachmentUtilities.getFilenameExtension(mName);
if (!TextUtils.isEmpty(extension) &&
Utility.arrayContains(Email.UNACCEPTABLE_ATTACHMENT_EXTENSIONS, extension)) {
canView = false;
@ -91,7 +91,7 @@ public class AttachmentInfo {
}
// Check for installable attachments by filename extension
extension = AttachmentProvider.getFilenameExtension(mName);
extension = AttachmentUtilities.getFilenameExtension(mName);
if (!TextUtils.isEmpty(extension) &&
Utility.arrayContains(Email.INSTALLABLE_ATTACHMENT_EXTENSIONS, extension)) {
int sideloadEnabled;
@ -134,9 +134,9 @@ public class AttachmentInfo {
* @return an Intent suitable for loading the attachment
*/
public Intent getAttachmentIntent(Context context, long accountId) {
Uri contentUri = AttachmentProvider.getAttachmentUri(accountId, mId);
Uri contentUri = AttachmentUtilities.getAttachmentUri(accountId, mId);
if (accountId > 0) {
contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(
contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
context.getContentResolver(), contentUri);
}
Intent intent = new Intent(Intent.ACTION_VIEW);
@ -154,6 +154,7 @@ public class AttachmentInfo {
return mAllowView || mAllowSave;
}
@Override
public String toString() {
return "{Attachment " + mId + ":" + mName + "," + mContentType + "," + mSize + "}";
}

View File

@ -21,7 +21,6 @@ import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
import com.android.email.mail.Folder.MessageRetrievalListener;
import com.android.email.mail.store.Pop3Store.Pop3Message;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
@ -34,6 +33,7 @@ import com.android.emailcommon.Api;
import com.android.emailcommon.service.EmailServiceStatus;
import com.android.emailcommon.service.IEmailService;
import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.app.Service;
import android.content.ContentResolver;
@ -197,7 +197,8 @@ public class Controller {
while (c.moveToNext()) {
long mailboxId = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
// Must delete attachments BEFORE messages
AttachmentProvider.deleteAllMailboxAttachmentFiles(mProviderContext, 0, mailboxId);
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mProviderContext, 0,
mailboxId);
resolver.delete(Message.CONTENT_URI, WHERE_MAILBOX_KEY,
new String[] {Long.toString(mailboxId)});
}
@ -720,7 +721,7 @@ public class Controller {
if (mailbox == null) return;
// 4. Drop non-essential data for the message (e.g. attachment files)
AttachmentProvider.deleteAllAttachmentFiles(mProviderContext, account.mId,
AttachmentUtilities.deleteAllAttachmentFiles(mProviderContext, account.mId,
messageId);
Uri uri = ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI,
@ -1003,7 +1004,8 @@ public class Controller {
public void deleteSyncedDataSync(long accountId) {
try {
// Delete synced attachments
AttachmentProvider.deleteAllAccountAttachmentFiles(mProviderContext, accountId);
AttachmentUtilities.deleteAllAccountAttachmentFiles(mProviderContext,
accountId);
// Delete synced email, leaving only an empty inbox. We do this in two phases:
// 1. Delete all non-inbox mailboxes (which will delete all of their messages)

View File

@ -19,20 +19,20 @@ package com.android.email;
import com.android.email.mail.Address;
import com.android.email.mail.Flag;
import com.android.email.mail.Message;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.Message.RecipientType;
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.MimeUtility;
import com.android.email.mail.internet.TextBody;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.AttachmentColumns;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.emailcommon.utility.AttachmentUtilities;
import org.apache.commons.io.IOUtils;
@ -41,7 +41,6 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
@ -147,108 +146,6 @@ public class LegacyConversions {
return true;
}
/**
* Copy body text (plain and/or HTML) from MimeMessage to provider Message
*/
public static boolean updateBodyFields(EmailContent.Body body,
EmailContent.Message localMessage, ArrayList<Part> viewables)
throws MessagingException {
body.mMessageKey = localMessage.mId;
StringBuffer sbHtml = null;
StringBuffer sbText = null;
StringBuffer sbHtmlReply = null;
StringBuffer sbTextReply = null;
StringBuffer sbIntroText = null;
for (Part viewable : viewables) {
String text = MimeUtility.getTextFromPart(viewable);
String[] replyTags = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART);
String replyTag = null;
if (replyTags != null && replyTags.length > 0) {
replyTag = replyTags[0];
}
// Deploy text as marked by the various tags
boolean isHtml = "text/html".equalsIgnoreCase(viewable.getMimeType());
if (replyTag != null) {
boolean isQuotedReply = BODY_QUOTED_PART_REPLY.equalsIgnoreCase(replyTag);
boolean isQuotedForward = BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(replyTag);
boolean isQuotedIntro = BODY_QUOTED_PART_INTRO.equalsIgnoreCase(replyTag);
if (isQuotedReply || isQuotedForward) {
if (isHtml) {
sbHtmlReply = appendTextPart(sbHtmlReply, text);
} else {
sbTextReply = appendTextPart(sbTextReply, text);
}
// Set message flags as well
localMessage.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
localMessage.mFlags |= isQuotedReply
? EmailContent.Message.FLAG_TYPE_REPLY
: EmailContent.Message.FLAG_TYPE_FORWARD;
continue;
}
if (isQuotedIntro) {
sbIntroText = appendTextPart(sbIntroText, text);
continue;
}
}
// Most of the time, just process regular body parts
if (isHtml) {
sbHtml = appendTextPart(sbHtml, text);
} else {
sbText = appendTextPart(sbText, text);
}
}
// write the combined data to the body part
if (!TextUtils.isEmpty(sbText)) {
String text = sbText.toString();
body.mTextContent = text;
localMessage.mSnippet = Snippet.fromPlainText(text);
}
if (!TextUtils.isEmpty(sbHtml)) {
String text = sbHtml.toString();
body.mHtmlContent = text;
if (localMessage.mSnippet == null) {
localMessage.mSnippet = Snippet.fromHtmlText(text);
}
}
if (sbHtmlReply != null && sbHtmlReply.length() != 0) {
body.mHtmlReply = sbHtmlReply.toString();
}
if (sbTextReply != null && sbTextReply.length() != 0) {
body.mTextReply = sbTextReply.toString();
}
if (sbIntroText != null && sbIntroText.length() != 0) {
body.mIntroText = sbIntroText.toString();
}
return true;
}
/**
* Helper function to append text to a StringBuffer, creating it if necessary.
* Optimization: The majority of the time we are *not* appending - we should have a path
* that deals with single strings.
*/
private static StringBuffer appendTextPart(StringBuffer sb, String newText) {
if (newText == null) {
return sb;
}
else if (sb == null) {
sb = new StringBuffer(newText);
} else {
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(newText);
}
return sb;
}
/**
* Copy attachments from MimeMessage to provider Message.
*
@ -392,11 +289,11 @@ public class LegacyConversions {
InputStream in = part.getBody().getInputStream();
File saveIn = AttachmentProvider.getAttachmentDirectory(context, accountId);
File saveIn = AttachmentUtilities.getAttachmentDirectory(context, accountId);
if (!saveIn.exists()) {
saveIn.mkdirs();
}
File saveAs = AttachmentProvider.getAttachmentFilename(context, accountId,
File saveAs = AttachmentUtilities.getAttachmentFilename(context, accountId,
attachmentId);
saveAs.createNewFile();
FileOutputStream out = new FileOutputStream(saveAs);
@ -405,7 +302,7 @@ public class LegacyConversions {
out.close();
// update the attachment with the extra information we now know
String contentUriString = AttachmentProvider.getAttachmentUri(
String contentUriString = AttachmentUtilities.getAttachmentUri(
accountId, attachmentId).toString();
localAttachment.mSize = copySize;

View File

@ -33,7 +33,6 @@ import com.android.email.mail.internet.MimeBodyPart;
import com.android.email.mail.internet.MimeHeader;
import com.android.email.mail.internet.MimeMultipart;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.AttachmentColumns;
@ -41,6 +40,8 @@ import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import com.android.email.provider.EmailContent.MessageColumns;
import com.android.email.provider.EmailContent.SyncColumns;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.ConversionUtilities;
import android.content.ContentResolver;
import android.content.ContentUris;
@ -300,7 +301,7 @@ public class MessagingController implements Runnable {
break;
default:
// Drop all attachment files related to this mailbox
AttachmentProvider.deleteAllMailboxAttachmentFiles(
AttachmentUtilities.deleteAllMailboxAttachmentFiles(
mContext, accountId, localInfo.mId);
// Delete the mailbox. Triggers will take care of
// related Message, Body and Attachment records.
@ -736,7 +737,8 @@ public class MessagingController implements Runnable {
// Delete associated data (attachment files)
// Attachment & Body records are auto-deleted when we delete the Message record
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId, infoToDelete.mId);
AttachmentUtilities.deleteAllAttachmentFiles(mContext, account.mId,
infoToDelete.mId);
// Delete the message itself
Uri uriToDelete = ContentUris.withAppendedId(
@ -1005,7 +1007,7 @@ public class MessagingController implements Runnable {
ArrayList<Part> attachments = new ArrayList<Part>();
MimeUtility.collectParts(message, viewables, attachments);
LegacyConversions.updateBodyFields(body, localMessage, viewables);
ConversionUtilities.updateBodyFields(body, localMessage, viewables);
// Commit the message & body to the local store immediately
saveOrUpdate(localMessage, context);
@ -2046,12 +2048,13 @@ public class MessagingController implements Runnable {
EmailContent.Message.restoreMessageWithId(mContext, messageId);
if (msg != null &&
((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0)) {
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId,
AttachmentUtilities.deleteAllAttachmentFiles(mContext, account.mId,
messageId);
}
resolver.update(syncedUri, moveToSentValues, null, null);
} else {
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId, messageId);
AttachmentUtilities.deleteAllAttachmentFiles(mContext, account.mId,
messageId);
Uri uri =
ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
resolver.delete(uri, null, null);

View File

@ -27,12 +27,12 @@ import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.mail.MessagingException;
import com.android.email.mail.internet.EmailHtmlUtil;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Body;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
import com.android.email.service.AttachmentDownloadService;
import com.android.emailcommon.utility.AttachmentUtilities;
import org.apache.commons.io.IOUtils;
@ -677,14 +677,14 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
return;
}
Attachment attachment = Attachment.restoreAttachmentWithId(mContext, info.mId);
Uri attachmentUri = AttachmentProvider.getAttachmentUri(mAccountId, attachment.mId);
Uri attachmentUri = AttachmentUtilities.getAttachmentUri(mAccountId, attachment.mId);
try {
File downloads = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS);
downloads.mkdirs();
File file = Utility.createUniqueFile(downloads, attachment.mFileName);
Uri contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(
Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
mContext.getContentResolver(), attachmentUri);
InputStream in = mContext.getContentResolver().openInputStream(contentUri);
OutputStream out = new FileOutputStream(file);
@ -1076,7 +1076,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
try {
return BitmapFactory.decodeStream(
mContext.getContentResolver().openInputStream(
AttachmentProvider.getAttachmentThumbnailUri(
AttachmentUtilities.getAttachmentThumbnailUri(
mAccountId, attachment.mId,
PREVIEW_ICON_WIDTH,
PREVIEW_ICON_HEIGHT)));

View File

@ -26,6 +26,7 @@ import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.service.MailService;
import com.android.emailcommon.service.PolicySet;
import com.android.emailcommon.service.SyncWindow;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
@ -66,7 +67,7 @@ public class AccountSetupOptions extends AccountSetupActivity implements OnClick
public static final int REQUEST_CODE_ACCEPT_POLICIES = 1;
/** Default sync window for new EAS accounts */
private static final int SYNC_WINDOW_EAS_DEFAULT = com.android.email.Account.SYNC_WINDOW_3_DAYS;
private static final int SYNC_WINDOW_EAS_DEFAULT = SyncWindow.SYNC_WINDOW_3_DAYS;
public static void actionOptions(Activity fromActivity) {
fromActivity.startActivity(new Intent(fromActivity, AccountSetupOptions.class));

View File

@ -20,14 +20,12 @@ import com.android.email.Email;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.AttachmentColumns;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.MessageColumns;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.AttachmentUtilities.Columns;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
@ -35,9 +33,7 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
@ -66,19 +62,6 @@ import java.util.List;
*/
public class AttachmentProvider extends ContentProvider {
public static final String AUTHORITY = "com.android.email.attachmentprovider";
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY);
private static final String FORMAT_RAW = "RAW";
private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
public static class AttachmentProviderColumns {
public static final String _ID = "_id";
public static final String DATA = "_data";
public static final String DISPLAY_NAME = "_display_name";
public static final String SIZE = "_size";
}
private static final String[] MIME_TYPE_PROJECTION = new String[] {
AttachmentColumns.MIME_TYPE, AttachmentColumns.FILENAME };
private static final int MIME_TYPE_COLUMN_MIME_TYPE = 0;
@ -87,47 +70,6 @@ public class AttachmentProvider extends ContentProvider {
private static final String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME,
AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI };
public static Uri getAttachmentUri(long accountId, long id) {
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_RAW)
.build();
}
public static Uri getAttachmentThumbnailUri(long accountId, long id,
int width, int height) {
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_THUMBNAIL)
.appendPath(Integer.toString(width))
.appendPath(Integer.toString(height))
.build();
}
/**
* Return the filename for a given attachment. This should be used by any code that is
* going to *write* attachments.
*
* This does not create or write the file, or even the directories. It simply builds
* the filename that should be used.
*/
public static File getAttachmentFilename(Context context, long accountId, long attachmentId) {
return new File(getAttachmentDirectory(context, accountId), Long.toString(attachmentId));
}
/**
* Return the directory for a given attachment. This should be used by any code that is
* going to *write* attachments.
*
* This does not create or write the directory. It simply builds the pathname that should be
* used.
*/
public static File getAttachmentDirectory(Context context, long accountId) {
return context.getDatabasePath(accountId + ".db_att");
}
@Override
public boolean onCreate() {
/*
@ -157,17 +99,17 @@ public class AttachmentProvider extends ContentProvider {
List<String> segments = uri.getPathSegments();
String id = segments.get(1);
String format = segments.get(2);
if (FORMAT_THUMBNAIL.equals(format)) {
if (AttachmentUtilities.FORMAT_THUMBNAIL.equals(format)) {
return "image/png";
} else {
uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id));
Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION,
null, null, null);
Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION, null,
null, null);
try {
if (c.moveToFirst()) {
String mimeType = c.getString(MIME_TYPE_COLUMN_MIME_TYPE);
String fileName = c.getString(MIME_TYPE_COLUMN_FILENAME);
mimeType = inferMimeType(fileName, mimeType);
mimeType = AttachmentUtilities.inferMimeType(fileName, mimeType);
return mimeType;
}
} finally {
@ -180,82 +122,6 @@ public class AttachmentProvider extends ContentProvider {
}
}
/**
* Helper to convert unknown or unmapped attachments to something useful based on filename
* extensions. The mime type is inferred based upon the table below. It's not perfect, but
* it helps.
*
* <pre>
* |---------------------------------------------------------|
* | E X T E N S I O N |
* |---------------------------------------------------------|
* | .eml | known(.png) | unknown(.abc) | none |
* | M |-----------------------------------------------------------------------|
* | I | none | msg/rfc822 | image/png | app/abc | app/oct-str |
* | M |-------------| (always | | | |
* | E | app/oct-str | overrides | | | |
* | T |-------------| | |-----------------------------|
* | Y | text/plain | | | text/plain |
* | P |-------------| |-------------------------------------------|
* | E | any/type | | any/type |
* |---|-----------------------------------------------------------------------|
* </pre>
*
* NOTE: Since mime types on Android are case-*sensitive*, return values are always in
* lower case.
*
* @param fileName The given filename
* @param mimeType The given mime type
* @return A likely mime type for the attachment
*/
public static String inferMimeType(final String fileName, final String mimeType) {
String resultType = null;
String fileExtension = getFilenameExtension(fileName);
boolean isTextPlain = "text/plain".equalsIgnoreCase(mimeType);
if ("eml".equals(fileExtension)) {
resultType = "message/rfc822";
} else {
boolean isGenericType =
isTextPlain || "application/octet-stream".equalsIgnoreCase(mimeType);
// If the given mime type is non-empty and non-generic, return it
if (isGenericType || TextUtils.isEmpty(mimeType)) {
if (!TextUtils.isEmpty(fileExtension)) {
// Otherwise, try to find a mime type based upon the file extension
resultType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
if (TextUtils.isEmpty(resultType)) {
// Finally, if original mimetype is text/plain, use it; otherwise synthesize
resultType = isTextPlain ? mimeType : "application/" + fileExtension;
}
}
} else {
resultType = mimeType;
}
}
// No good guess could be made; use an appropriate generic type
if (TextUtils.isEmpty(resultType)) {
resultType = isTextPlain ? "text/plain" : "application/octet-stream";
}
return resultType.toLowerCase();
}
/**
* Extract and return filename's extension, converted to lower case, and not including the "."
*
* @return extension, or null if not found (or null/empty filename)
*/
public static String getFilenameExtension(String fileName) {
String extension = null;
if (!TextUtils.isEmpty(fileName)) {
int lastDot = fileName.lastIndexOf('.');
if ((lastDot > 0) && (lastDot < fileName.length() - 1)) {
extension = fileName.substring(lastDot + 1).toLowerCase();
}
}
return extension;
}
/**
* Open an attachment file. There are two "modes" - "raw", which returns an actual file,
* and "thumbnail", which attempts to generate a thumbnail image.
@ -275,17 +141,17 @@ public class AttachmentProvider extends ContentProvider {
String accountId = segments.get(0);
String id = segments.get(1);
String format = segments.get(2);
if (FORMAT_THUMBNAIL.equals(format)) {
if (AttachmentUtilities.FORMAT_THUMBNAIL.equals(format)) {
int width = Integer.parseInt(segments.get(3));
int height = Integer.parseInt(segments.get(4));
String filename = "thmb_" + accountId + "_" + id;
File dir = getContext().getCacheDir();
File file = new File(dir, filename);
if (!file.exists()) {
Uri attachmentUri =
Uri attachmentUri = AttachmentUtilities.
getAttachmentUri(Long.parseLong(accountId), Long.parseLong(id));
Cursor c = query(attachmentUri,
new String[] { AttachmentProviderColumns.DATA }, null, null, null);
new String[] { Columns.DATA }, null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
@ -355,8 +221,8 @@ public class AttachmentProvider extends ContentProvider {
if (projection == null) {
projection =
new String[] {
AttachmentProviderColumns._ID,
AttachmentProviderColumns.DATA,
Columns._ID,
Columns.DATA,
};
}
@ -387,16 +253,16 @@ public class AttachmentProvider extends ContentProvider {
Object[] values = new Object[projection.length];
for (int i = 0, count = projection.length; i < count; i++) {
String column = projection[i];
if (AttachmentProviderColumns._ID.equals(column)) {
if (Columns._ID.equals(column)) {
values[i] = id;
}
else if (AttachmentProviderColumns.DATA.equals(column)) {
else if (Columns.DATA.equals(column)) {
values[i] = contentUri;
}
else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) {
else if (Columns.DISPLAY_NAME.equals(column)) {
values[i] = name;
}
else if (AttachmentProviderColumns.SIZE.equals(column)) {
else if (Columns.SIZE.equals(column)) {
values[i] = size;
}
}
@ -432,101 +298,6 @@ public class AttachmentProvider extends ContentProvider {
}
}
/**
* Resolve attachment id to content URI. Returns the resolved content URI (from the attachment
* DB) or, if not found, simply returns the incoming value.
*
* @param attachmentUri
* @return resolved content URI
*
* TODO: Throws an SQLite exception on a missing DB file (e.g. unknown URI) instead of just
* returning the incoming uri, as it should.
*/
public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) {
Cursor c = resolver.query(attachmentUri,
new String[] { AttachmentProvider.AttachmentProviderColumns.DATA },
null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
final String strUri = c.getString(0);
if (strUri != null) {
return Uri.parse(strUri);
} else {
Email.log("AttachmentProvider: attachment with null contentUri");
}
}
} finally {
c.close();
}
}
return attachmentUri;
}
/**
* In support of deleting a message, find all attachments and delete associated attachment
* files.
* @param context
* @param accountId the account for the message
* @param messageId the message
*/
public static void deleteAllAttachmentFiles(Context context, long accountId, long messageId) {
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
Cursor c = context.getContentResolver().query(uri, Attachment.ID_PROJECTION,
null, null, null);
try {
while (c.moveToNext()) {
long attachmentId = c.getLong(Attachment.ID_PROJECTION_COLUMN);
File attachmentFile = getAttachmentFilename(context, accountId, attachmentId);
// Note, delete() throws no exceptions for basic FS errors (e.g. file not found)
// it just returns false, which we ignore, and proceed to the next file.
// This entire loop is best-effort only.
attachmentFile.delete();
}
} finally {
c.close();
}
}
/**
* In support of deleting a mailbox, find all messages and delete their attachments.
*
* @param context
* @param accountId the account for the mailbox
* @param mailboxId the mailbox for the messages
*/
public static void deleteAllMailboxAttachmentFiles(Context context, long accountId,
long mailboxId) {
Cursor c = context.getContentResolver().query(Message.CONTENT_URI,
Message.ID_COLUMN_PROJECTION, MessageColumns.MAILBOX_KEY + "=?",
new String[] { Long.toString(mailboxId) }, null);
try {
while (c.moveToNext()) {
long messageId = c.getLong(Message.ID_PROJECTION_COLUMN);
deleteAllAttachmentFiles(context, accountId, messageId);
}
} finally {
c.close();
}
}
/**
* In support of deleting or wiping an account, delete all related attachments.
*
* @param context
* @param accountId the account to scrub
*/
public static void deleteAllAccountAttachmentFiles(Context context, long accountId) {
File[] files = getAttachmentDirectory(context, accountId).listFiles();
if (files == null) return;
for (File file : files) {
boolean result = file.delete();
if (!result) {
Log.e(Email.LOG_TAG, "Failed to delete attachment file " + file.getName());
}
}
}
/**
* Need this to suppress warning in unit tests.
*/

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2011 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.service;
import com.android.email.AccountBackupRestore;
import com.android.email.NotificationController;
import com.android.email.ResourceHelper;
import com.android.emailcommon.service.IAccountService;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class AccountService extends Service {
private Context mContext;
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
@Override
public void notifyLoginFailed(long accountId) throws RemoteException {
NotificationController.getInstance(mContext).showLoginFailedNotification(accountId);
}
@Override
public void notifyLoginSucceeded(long accountId) throws RemoteException {
NotificationController.getInstance(mContext).cancelLoginFailedNotification(accountId);
}
@Override
public void notifyNewMessages(long accountId) throws RemoteException {
MailService.actionNotifyNewMessages(mContext, accountId);
}
@Override
public void restoreAccountsIfNeeded() throws RemoteException {
AccountBackupRestore.restoreAccountsIfNeeded(mContext);
}
@Override
public void accountDeleted() throws RemoteException {
MailService.accountDeleted(mContext);
}
@Override
public int getAccountColor(long accountId) throws RemoteException {
return ResourceHelper.getInstance(mContext).getAccountColor(accountId);
}
};
@Override
public IBinder onBind(Intent intent) {
if (mContext == null) {
mContext = this;
}
return mBinder;
}
}

View File

@ -23,7 +23,6 @@ import com.android.email.NotificationController;
import com.android.email.Utility;
import com.android.email.Controller.ControllerService;
import com.android.email.ExchangeUtils.NullEmailService;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
@ -31,6 +30,7 @@ import com.android.email.provider.EmailContent.Message;
import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.service.EmailServiceStatus;
import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.exchange.ExchangeService;
import android.accounts.AccountManager;
@ -427,7 +427,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
*/
private void startDownload(Class<? extends Service> serviceClass, DownloadRequest req)
throws RemoteException {
File file = AttachmentProvider.getAttachmentFilename(mContext, req.accountId,
File file = AttachmentUtilities.getAttachmentFilename(mContext, req.accountId,
req.attachmentId);
req.startTime = System.currentTimeMillis();
req.inProgress = true;
@ -437,7 +437,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
EmailServiceProxy proxy =
new EmailServiceProxy(mContext, serviceClass, mServiceCallback);
proxy.loadAttachment(req.attachmentId, file.getAbsolutePath(),
AttachmentProvider.getAttachmentUri(req.accountId, req.attachmentId)
AttachmentUtilities.getAttachmentUri(req.accountId, req.attachmentId)
.toString(), req.priority != PRIORITY_FOREGROUND);
// Lazily initialize our (reusable) pending intent
if (mWatchdogPendingIntent == null) {
@ -949,7 +949,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
if (att.mMimeType != null) {
pw.print(att.mMimeType);
} else {
pw.print(AttachmentProvider.inferMimeType(fileName, null));
pw.print(AttachmentUtilities.inferMimeType(fileName, null));
pw.print(" [inferred]");
}
pw.println(" Size: " + att.mSize);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2011 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.emailcommon.service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class AccountServiceProxy extends ServiceProxy implements IAccountService {
public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT";
public static final int DEFAULT_ACCOUNT_COLOR = 0xFF0000FF;
private IAccountService mService = null;
private Object mReturn;
public AccountServiceProxy(Context _context) {
super(_context, new Intent(ACCOUNT_INTENT));
}
@Override
public void onConnected(IBinder binder) {
mService = IAccountService.Stub.asInterface(binder);
}
public IBinder asBinder() {
return null;
}
@Override
public void notifyLoginFailed(final long accountId) throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException {
mService.notifyLoginFailed(accountId);
}
}, "notifyLoginFailed");
}
@Override
public void notifyLoginSucceeded(final long accountId) throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException {
mService.notifyLoginSucceeded(accountId);
}
}, "notifyLoginSucceeded");
}
@Override
public void notifyNewMessages(final long accountId) throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException {
mService.notifyNewMessages(accountId);
}
}, "notifyNewMessages");
}
@Override
public void accountDeleted() throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException {
mService.accountDeleted();
}
}, "accountDeleted");
}
@Override
public void restoreAccountsIfNeeded() throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException {
mService.restoreAccountsIfNeeded();
}
}, "restoreAccountsIfNeeded");
}
@Override
public int getAccountColor(final long accountId) throws RemoteException {
setTask(new ProxyTask() {
public void run() throws RemoteException{
mReturn = mService.getAccountColor(accountId);
}
}, "getAccountColor");
waitForCompletion();
if (mReturn == null) {
return DEFAULT_ACCOUNT_COLOR;
} else {
return (Integer)mReturn;
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2011 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.emailcommon.service;
interface IAccountService {
oneway void notifyLoginFailed(long accountId);
oneway void notifyLoginSucceeded(long accountId);
oneway void notifyNewMessages(long accountId);
void accountDeleted();
void restoreAccountsIfNeeded();
int getAccountColor(long accountId);
}

View File

@ -223,6 +223,5 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
}
throw new IllegalStateException("PolicyService transaction failed");
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2011 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.emailcommon.service;
public class SyncWindow {
public static final int SYNC_WINDOW_USER = -1;
public static final int SYNC_WINDOW_1_DAY = 1;
public static final int SYNC_WINDOW_3_DAYS = 2;
public static final int SYNC_WINDOW_1_WEEK = 3;
public static final int SYNC_WINDOW_2_WEEKS = 4;
public static final int SYNC_WINDOW_1_MONTH = 5;
public static final int SYNC_WINDOW_ALL = 6;
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2011 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.emailcommon.utility;
import com.android.email.Email;
import com.android.email.provider.EmailContent.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.util.List;
public class AccountReconciler {
/**
* Compare our account list (obtained from EmailProvider) with the account list owned by
* AccountManager. If there are any orphans (an account in one list without a corresponding
* account in the other list), delete the orphan, as these must remain in sync.
*
* Note that the duplication of account information is caused by the Email application's
* incomplete integration with AccountManager.
*
* This function may not be called from the main/UI thread, because it makes blocking calls
* into the account manager.
*
* @param context The context in which to operate
* @param emailProviderAccounts the exchange provider accounts to work from
* @param accountManagerAccounts The account manager accounts to work from
* @param resolver the content resolver for making provider updates (injected for testability)
*/
public static boolean reconcileAccounts(Context context,
List<Account> emailProviderAccounts, android.accounts.Account[] accountManagerAccounts,
ContentResolver resolver) {
// First, look through our EmailProvider accounts to make sure there's a corresponding
// AccountManager account
boolean accountsDeleted = false;
for (Account providerAccount: emailProviderAccounts) {
String providerAccountName = providerAccount.mEmailAddress;
boolean found = false;
for (android.accounts.Account accountManagerAccount: accountManagerAccounts) {
if (accountManagerAccount.name.equalsIgnoreCase(providerAccountName)) {
found = true;
break;
}
}
if (!found) {
if ((providerAccount.mFlags & Account.FLAGS_INCOMPLETE) != 0) {
if (Email.DEBUG) {
Log.d(Email.LOG_TAG,
"Account reconciler noticed incomplete account; ignoring");
}
continue;
}
// This account has been deleted in the AccountManager!
Log.d(Email.LOG_TAG, "Account deleted in AccountManager; deleting from provider: " +
providerAccountName);
// TODO This will orphan downloaded attachments; need to handle this
resolver.delete(ContentUris.withAppendedId(Account.CONTENT_URI,
providerAccount.mId), null, null);
accountsDeleted = true;
}
}
// Now, look through AccountManager accounts to make sure we have a corresponding cached EAS
// account from EmailProvider
for (android.accounts.Account accountManagerAccount: accountManagerAccounts) {
String accountManagerAccountName = accountManagerAccount.name;
boolean found = false;
for (Account cachedEasAccount: emailProviderAccounts) {
if (cachedEasAccount.mEmailAddress.equalsIgnoreCase(accountManagerAccountName)) {
found = true;
}
}
if (!found) {
// This account has been deleted from the EmailProvider database
Log.d(Email.LOG_TAG,
"Account deleted from provider; deleting from AccountManager: " +
accountManagerAccountName);
// Delete the account
AccountManagerFuture<Boolean> blockingResult = AccountManager.get(context)
.removeAccount(accountManagerAccount, null, null);
try {
// Note: All of the potential errors from removeAccount() are simply logged
// here, as there is nothing to actually do about them.
blockingResult.getResult();
} catch (OperationCanceledException e) {
Log.w(Email.LOG_TAG, e.toString());
} catch (AuthenticatorException e) {
Log.w(Email.LOG_TAG, e.toString());
} catch (IOException e) {
Log.w(Email.LOG_TAG, e.toString());
}
accountsDeleted = true;
}
}
return accountsDeleted;
}
}

View File

@ -0,0 +1,258 @@
/*
* Copyright (C) 2011 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.emailcommon.utility;
import com.android.email.Email;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.MessageColumns;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
public class AttachmentUtilities {
public static final String AUTHORITY = "com.android.email.attachmentprovider";
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY);
public static final String FORMAT_RAW = "RAW";
public static final String FORMAT_THUMBNAIL = "THUMBNAIL";
public static class Columns {
public static final String _ID = "_id";
public static final String DATA = "_data";
public static final String DISPLAY_NAME = "_display_name";
public static final String SIZE = "_size";
}
public static Uri getAttachmentUri(long accountId, long id) {
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_RAW)
.build();
}
public static Uri getAttachmentThumbnailUri(long accountId, long id,
int width, int height) {
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_THUMBNAIL)
.appendPath(Integer.toString(width))
.appendPath(Integer.toString(height))
.build();
}
/**
* Return the filename for a given attachment. This should be used by any code that is
* going to *write* attachments.
*
* This does not create or write the file, or even the directories. It simply builds
* the filename that should be used.
*/
public static File getAttachmentFilename(Context context, long accountId, long attachmentId) {
return new File(getAttachmentDirectory(context, accountId), Long.toString(attachmentId));
}
/**
* Return the directory for a given attachment. This should be used by any code that is
* going to *write* attachments.
*
* This does not create or write the directory. It simply builds the pathname that should be
* used.
*/
public static File getAttachmentDirectory(Context context, long accountId) {
return context.getDatabasePath(accountId + ".db_att");
}
/**
* Helper to convert unknown or unmapped attachments to something useful based on filename
* extensions. The mime type is inferred based upon the table below. It's not perfect, but
* it helps.
*
* <pre>
* |---------------------------------------------------------|
* | E X T E N S I O N |
* |---------------------------------------------------------|
* | .eml | known(.png) | unknown(.abc) | none |
* | M |-----------------------------------------------------------------------|
* | I | none | msg/rfc822 | image/png | app/abc | app/oct-str |
* | M |-------------| (always | | | |
* | E | app/oct-str | overrides | | | |
* | T |-------------| | |-----------------------------|
* | Y | text/plain | | | text/plain |
* | P |-------------| |-------------------------------------------|
* | E | any/type | | any/type |
* |---|-----------------------------------------------------------------------|
* </pre>
*
* NOTE: Since mime types on Android are case-*sensitive*, return values are always in
* lower case.
*
* @param fileName The given filename
* @param mimeType The given mime type
* @return A likely mime type for the attachment
*/
public static String inferMimeType(final String fileName, final String mimeType) {
String resultType = null;
String fileExtension = getFilenameExtension(fileName);
boolean isTextPlain = "text/plain".equalsIgnoreCase(mimeType);
if ("eml".equals(fileExtension)) {
resultType = "message/rfc822";
} else {
boolean isGenericType =
isTextPlain || "application/octet-stream".equalsIgnoreCase(mimeType);
// If the given mime type is non-empty and non-generic, return it
if (isGenericType || TextUtils.isEmpty(mimeType)) {
if (!TextUtils.isEmpty(fileExtension)) {
// Otherwise, try to find a mime type based upon the file extension
resultType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
if (TextUtils.isEmpty(resultType)) {
// Finally, if original mimetype is text/plain, use it; otherwise synthesize
resultType = isTextPlain ? mimeType : "application/" + fileExtension;
}
}
} else {
resultType = mimeType;
}
}
// No good guess could be made; use an appropriate generic type
if (TextUtils.isEmpty(resultType)) {
resultType = isTextPlain ? "text/plain" : "application/octet-stream";
}
return resultType.toLowerCase();
}
/**
* Extract and return filename's extension, converted to lower case, and not including the "."
*
* @return extension, or null if not found (or null/empty filename)
*/
public static String getFilenameExtension(String fileName) {
String extension = null;
if (!TextUtils.isEmpty(fileName)) {
int lastDot = fileName.lastIndexOf('.');
if ((lastDot > 0) && (lastDot < fileName.length() - 1)) {
extension = fileName.substring(lastDot + 1).toLowerCase();
}
}
return extension;
}
/**
* Resolve attachment id to content URI. Returns the resolved content URI (from the attachment
* DB) or, if not found, simply returns the incoming value.
*
* @param attachmentUri
* @return resolved content URI
*
* TODO: Throws an SQLite exception on a missing DB file (e.g. unknown URI) instead of just
* returning the incoming uri, as it should.
*/
public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) {
Cursor c = resolver.query(attachmentUri,
new String[] { Columns.DATA },
null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
final String strUri = c.getString(0);
if (strUri != null) {
return Uri.parse(strUri);
}
}
} finally {
c.close();
}
}
return attachmentUri;
}
/**
* In support of deleting a message, find all attachments and delete associated attachment
* files.
* @param context
* @param accountId the account for the message
* @param messageId the message
*/
public static void deleteAllAttachmentFiles(Context context, long accountId, long messageId) {
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
Cursor c = context.getContentResolver().query(uri, Attachment.ID_PROJECTION,
null, null, null);
try {
while (c.moveToNext()) {
long attachmentId = c.getLong(Attachment.ID_PROJECTION_COLUMN);
File attachmentFile = getAttachmentFilename(context, accountId, attachmentId);
// Note, delete() throws no exceptions for basic FS errors (e.g. file not found)
// it just returns false, which we ignore, and proceed to the next file.
// This entire loop is best-effort only.
attachmentFile.delete();
}
} finally {
c.close();
}
}
/**
* In support of deleting a mailbox, find all messages and delete their attachments.
*
* @param context
* @param accountId the account for the mailbox
* @param mailboxId the mailbox for the messages
*/
public static void deleteAllMailboxAttachmentFiles(Context context, long accountId,
long mailboxId) {
Cursor c = context.getContentResolver().query(Message.CONTENT_URI,
Message.ID_COLUMN_PROJECTION, MessageColumns.MAILBOX_KEY + "=?",
new String[] { Long.toString(mailboxId) }, null);
try {
while (c.moveToNext()) {
long messageId = c.getLong(Message.ID_PROJECTION_COLUMN);
deleteAllAttachmentFiles(context, accountId, messageId);
}
} finally {
c.close();
}
}
/**
* In support of deleting or wiping an account, delete all related attachments.
*
* @param context
* @param accountId the account to scrub
*/
public static void deleteAllAccountAttachmentFiles(Context context, long accountId) {
File[] files = getAttachmentDirectory(context, accountId).listFiles();
if (files == null) return;
for (File file : files) {
boolean result = file.delete();
if (!result) {
Log.e(Email.LOG_TAG, "Failed to delete attachment file " + file.getName());
}
}
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2011 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.emailcommon.utility;
import com.android.email.Snippet;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.internet.MimeHeader;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.provider.EmailContent;
import android.text.TextUtils;
import java.util.ArrayList;
public class ConversionUtilities {
/**
* Values for HEADER_ANDROID_BODY_QUOTED_PART to tag body parts
*/
public static final String BODY_QUOTED_PART_REPLY = "quoted-reply";
public static final String BODY_QUOTED_PART_FORWARD = "quoted-forward";
public static final String BODY_QUOTED_PART_INTRO = "quoted-intro";
/**
* Helper function to append text to a StringBuffer, creating it if necessary.
* Optimization: The majority of the time we are *not* appending - we should have a path
* that deals with single strings.
*/
private static StringBuffer appendTextPart(StringBuffer sb, String newText) {
if (newText == null) {
return sb;
}
else if (sb == null) {
sb = new StringBuffer(newText);
} else {
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(newText);
}
return sb;
}
/**
* Copy body text (plain and/or HTML) from MimeMessage to provider Message
*/
public static boolean updateBodyFields(EmailContent.Body body,
EmailContent.Message localMessage, ArrayList<Part> viewables)
throws MessagingException {
body.mMessageKey = localMessage.mId;
StringBuffer sbHtml = null;
StringBuffer sbText = null;
StringBuffer sbHtmlReply = null;
StringBuffer sbTextReply = null;
StringBuffer sbIntroText = null;
for (Part viewable : viewables) {
String text = MimeUtility.getTextFromPart(viewable);
String[] replyTags = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART);
String replyTag = null;
if (replyTags != null && replyTags.length > 0) {
replyTag = replyTags[0];
}
// Deploy text as marked by the various tags
boolean isHtml = "text/html".equalsIgnoreCase(viewable.getMimeType());
if (replyTag != null) {
boolean isQuotedReply = BODY_QUOTED_PART_REPLY.equalsIgnoreCase(replyTag);
boolean isQuotedForward = BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(replyTag);
boolean isQuotedIntro = BODY_QUOTED_PART_INTRO.equalsIgnoreCase(replyTag);
if (isQuotedReply || isQuotedForward) {
if (isHtml) {
sbHtmlReply = appendTextPart(sbHtmlReply, text);
} else {
sbTextReply = appendTextPart(sbTextReply, text);
}
// Set message flags as well
localMessage.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
localMessage.mFlags |= isQuotedReply
? EmailContent.Message.FLAG_TYPE_REPLY
: EmailContent.Message.FLAG_TYPE_FORWARD;
continue;
}
if (isQuotedIntro) {
sbIntroText = appendTextPart(sbIntroText, text);
continue;
}
}
// Most of the time, just process regular body parts
if (isHtml) {
sbHtml = appendTextPart(sbHtml, text);
} else {
sbText = appendTextPart(sbText, text);
}
}
// write the combined data to the body part
if (!TextUtils.isEmpty(sbText)) {
String text = sbText.toString();
body.mTextContent = text;
localMessage.mSnippet = Snippet.fromPlainText(text);
}
if (!TextUtils.isEmpty(sbHtml)) {
String text = sbHtml.toString();
body.mHtmlContent = text;
if (localMessage.mSnippet == null) {
localMessage.mSnippet = Snippet.fromHtmlText(text);
}
}
if (sbHtmlReply != null && sbHtmlReply.length() != 0) {
body.mHtmlReply = sbHtmlReply.toString();
}
if (sbTextReply != null && sbTextReply.length() != 0) {
body.mTextReply = sbTextReply.toString();
}
if (sbIntroText != null && sbIntroText.length() != 0) {
body.mIntroText = sbIntroText.toString();
}
return true;
}
}

View File

@ -17,9 +17,7 @@
package com.android.exchange;
import com.android.email.AccountBackupRestore;
import com.android.email.Email;
import com.android.email.NotificationController;
import com.android.email.Utility;
import com.android.email.mail.transport.SSLUtils;
import com.android.email.provider.EmailContent;
@ -32,11 +30,12 @@ import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.SyncColumns;
import com.android.email.service.MailService;
import com.android.emailcommon.Api;
import com.android.emailcommon.service.AccountServiceProxy;
import com.android.emailcommon.service.EmailServiceStatus;
import com.android.emailcommon.service.IEmailService;
import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.utility.AccountReconciler;
import com.android.exchange.adapter.CalendarSyncAdapter;
import com.android.exchange.adapter.ContactsSyncAdapter;
import com.android.exchange.utility.FileLogger;
@ -1046,8 +1045,15 @@ public class ExchangeService extends Service implements Runnable {
// list, which would cause the deletion of all of our accounts
AccountList accountList = collectEasAccounts(context, new AccountList());
alwaysLog("Reconciling accounts...");
MailService.reconcileAccountsWithAccountManager(context, accountList, accountMgrList,
false, context.getContentResolver());
boolean accountsDeleted = AccountReconciler.reconcileAccounts(context, accountList,
accountMgrList, context.getContentResolver());
if (accountsDeleted) {
try {
new AccountServiceProxy(context).accountDeleted();
} catch (RemoteException e) {
// ?
}
}
}
public static void log(String str) {
@ -1749,13 +1755,22 @@ public class ExchangeService extends Service implements Runnable {
@Override
public void run() {
synchronized (sSyncLock) {
// ExchangeService cannot start unless we can connect to AccountService
if (!new AccountServiceProxy(ExchangeService.this).test()) {
log("!!! Email application not found; stopping self");
stopSelf();
}
// Restore accounts, if it has not happened already
AccountBackupRestore.restoreAccountsIfNeeded(ExchangeService.this);
try {
new AccountServiceProxy(ExchangeService.this).restoreAccountsIfNeeded();
} catch (RemoteException e) {
// If we can't restore accounts, don't run
return;
}
// Run the reconciler and clean up any mismatched accounts - if we weren't
// running when accounts were deleted, it won't have been called.
runAccountReconcilerSync(ExchangeService.this);
// Update other services depending on final account configuration
Email.setServicesEnabledSync(ExchangeService.this);
maybeStartExchangeServiceThread();
if (sServiceThread == null) {
log("!!! EAS ExchangeService, stopping self");
@ -2385,8 +2400,12 @@ public class ExchangeService extends Service implements Runnable {
if (account == null) return;
if (exchangeService.releaseSyncHolds(exchangeService,
AbstractSyncService.EXIT_LOGIN_FAILURE, account)) {
NotificationController.getInstance(exchangeService)
.cancelLoginFailedNotification(accountId);
try {
new AccountServiceProxy(exchangeService).notifyLoginSucceeded(
accountId);
} catch (RemoteException e) {
// No harm if the notification fails
}
}
}
@ -2413,8 +2432,11 @@ public class ExchangeService extends Service implements Runnable {
break;
// These errors are not retried automatically
case AbstractSyncService.EXIT_LOGIN_FAILURE:
NotificationController.getInstance(exchangeService)
.showLoginFailedNotification(m.mAccountKey);
try {
new AccountServiceProxy(exchangeService).notifyLoginFailed(m.mAccountKey);
} catch (RemoteException e) {
// ? Anything to do?
}
// Fall through
case AbstractSyncService.EXIT_SECURITY_FAILURE:
case AbstractSyncService.EXIT_EXCEPTION:

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2011 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.exchange;
import com.android.email.provider.EmailContent.Account;
import com.android.emailcommon.service.PolicyServiceProxy;
import com.android.emailcommon.service.PolicySet;
import android.content.Context;
import android.os.RemoteException;
public class PolicyServiceDelegate {
public static boolean isActive(Context context, PolicySet policies) {
try {
return new PolicyServiceProxy(context).isActive(policies);
} catch (RemoteException e) {
}
return false;
}
public static void policiesRequired(Context context, long accountId) {
try {
new PolicyServiceProxy(context).policiesRequired(accountId);
} catch (RemoteException e) {
throw new IllegalStateException("PolicyService transaction failed");
}
}
public static void updatePolicies(Context context, long accountId) {
try {
new PolicyServiceProxy(context).updatePolicies(accountId);
} catch (RemoteException e) {
throw new IllegalStateException("PolicyService transaction failed");
}
}
public static void setAccountHoldFlag(Context context, Account account, boolean newState) {
try {
new PolicyServiceProxy(context).setAccountHoldFlag(account.mId, newState);
} catch (RemoteException e) {
throw new IllegalStateException("PolicyService transaction failed");
}
}
public static boolean isActiveAdmin(Context context) {
try {
return new PolicyServiceProxy(context).isActiveAdmin();
} catch (RemoteException e) {
}
return false;
}
public static void remoteWipe(Context context) {
try {
new PolicyServiceProxy(context).remoteWipe();
} catch (RemoteException e) {
throw new IllegalStateException("PolicyService transaction failed");
}
}
public static boolean isSupported(Context context, PolicySet policies) {
try {
return new PolicyServiceProxy(context).isSupported(policies);
} catch (RemoteException e) {
}
return false;
}
public static PolicySet clearUnsupportedPolicies(Context context, PolicySet policies) {
try {
return new PolicyServiceProxy(context).clearUnsupportedPolicies(policies);
} catch (RemoteException e) {
}
throw new IllegalStateException("PolicyService transaction failed");
}
}

View File

@ -17,7 +17,6 @@
package com.android.exchange.adapter;
import com.android.email.LegacyConversions;
import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.mail.MeetingInfo;
@ -26,8 +25,8 @@ import com.android.email.mail.PackedString;
import com.android.email.mail.Part;
import com.android.email.mail.internet.MimeMessage;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Attachment;
@ -36,8 +35,10 @@ import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.MessageColumns;
import com.android.email.provider.EmailContent.SyncColumns;
import com.android.email.provider.EmailProvider;
import com.android.email.service.MailService;
import com.android.emailcommon.service.AccountServiceProxy;
import com.android.emailcommon.service.SyncWindow;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.ConversionUtilities;
import com.android.exchange.Eas;
import com.android.exchange.EasSyncService;
import com.android.exchange.MessageMoveRequest;
@ -117,22 +118,23 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
mService.clearRequests();
mFetchRequestList.clear();
// Delete attachments...
AttachmentProvider.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId);
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId,
mMailbox.mId);
}
private String getEmailFilter() {
switch (mAccount.mSyncLookback) {
case com.android.email.Account.SYNC_WINDOW_1_DAY:
case SyncWindow.SYNC_WINDOW_1_DAY:
return Eas.FILTER_1_DAY;
case com.android.email.Account.SYNC_WINDOW_3_DAYS:
case SyncWindow.SYNC_WINDOW_3_DAYS:
return Eas.FILTER_3_DAYS;
case com.android.email.Account.SYNC_WINDOW_1_WEEK:
case SyncWindow.SYNC_WINDOW_1_WEEK:
return Eas.FILTER_1_WEEK;
case com.android.email.Account.SYNC_WINDOW_2_WEEKS:
case SyncWindow.SYNC_WINDOW_2_WEEKS:
return Eas.FILTER_2_WEEKS;
case com.android.email.Account.SYNC_WINDOW_1_MONTH:
case SyncWindow.SYNC_WINDOW_1_MONTH:
return Eas.FILTER_1_MONTH;
case com.android.email.Account.SYNC_WINDOW_ALL:
case SyncWindow.SYNC_WINDOW_ALL:
return Eas.FILTER_ALL;
default:
return Eas.FILTER_1_WEEK;
@ -496,7 +498,7 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
MimeUtility.collectParts(mimeMessage, viewables, attachments);
Body tempBody = new Body();
// updateBodyFields fills in the content fields of the Body
LegacyConversions.updateBodyFields(tempBody, msg, viewables);
ConversionUtilities.updateBodyFields(tempBody, msg, viewables);
// But we need them in the message itself for handling during commit()
msg.mHtml = tempBody.mHtmlContent;
msg.mText = tempBody.mTextContent;
@ -770,7 +772,7 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
for (Long id : deletedEmails) {
ops.add(ContentProviderOperation.newDelete(
ContentUris.withAppendedId(Message.CONTENT_URI, id)).build());
AttachmentProvider.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
}
if (!changedEmails.isEmpty()) {
@ -822,7 +824,11 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount);
Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId);
mContentResolver.update(uri, cv, null, null);
MailService.actionNotifyNewMessages(mContext, mAccount.mId);
try {
new AccountServiceProxy(mService.mContext).notifyNewMessages(mAccount.mId);
} catch (RemoteException e) {
// ? Anything to do here?
}
}
}
}

View File

@ -18,13 +18,13 @@
package com.android.exchange.adapter;
import com.android.email.Utility;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import com.android.email.provider.EmailProvider;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.exchange.Eas;
import com.android.exchange.ExchangeService;
import com.android.exchange.MockParserStream;
@ -187,7 +187,7 @@ public class FolderSyncParser extends AbstractSyncParser {
ops.add(ContentProviderOperation.newDelete(
ContentUris.withAppendedId(Mailbox.CONTENT_URI,
c.getLong(0))).build());
AttachmentProvider.deleteAllMailboxAttachmentFiles(mContext,
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext,
mAccountId, mMailbox.mId);
}
} finally {

View File

@ -18,7 +18,6 @@ package com.android.exchange.utility;
import com.android.email.Email;
import com.android.email.R;
import com.android.email.ResourceHelper;
import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.provider.EmailContent;
@ -26,6 +25,7 @@ import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
import com.android.emailcommon.service.AccountServiceProxy;
import com.android.exchange.Eas;
import com.android.exchange.EasSyncService;
import com.android.exchange.ExchangeService;
@ -1215,8 +1215,12 @@ public class CalendarUtilities {
cv.put(Calendars.ORGANIZER_CAN_RESPOND, 0);
// TODO Coordinate account colors w/ Calendar, if possible
// Make Email account color opaque
int color = ResourceHelper.getInstance(service.mContext).getAccountColor(account.mId);
int color = AccountServiceProxy.DEFAULT_ACCOUNT_COLOR;
try {
color = new AccountServiceProxy(service.mContext).getAccountColor(account.mId);
} catch (RemoteException e) {
// Use the default
}
cv.put(Calendars.COLOR, color);
cv.put(Calendars.TIMEZONE, Time.getCurrentTimezone());
cv.put(Calendars.ACCESS_LEVEL, Calendars.OWNER_ACCESS);

View File

@ -20,6 +20,7 @@ import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.ContentCache;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.content.ContentProvider;
import android.content.ContentResolver;
@ -37,8 +38,6 @@ import android.test.mock.MockCursor;
import java.io.File;
import junit.framework.Assert;
/**
* Helper classes (and possibly methods) for database related tests.
*/
@ -225,7 +224,7 @@ public final class DBTestHelper {
final AttachmentProvider ap = new AttachmentProvider();
ap.attachInfo(providerContext, null);
resolver.addProvider(AttachmentProvider.AUTHORITY, ap);
resolver.addProvider(AttachmentUtilities.AUTHORITY, ap);
ContentCache.invalidateAllCachesForTest();

View File

@ -20,21 +20,22 @@ import com.android.email.mail.Address;
import com.android.email.mail.BodyPart;
import com.android.email.mail.Flag;
import com.android.email.mail.Message;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.MessageTestUtils;
import com.android.email.mail.MessageTestUtils.MessageBuilder;
import com.android.email.mail.MessageTestUtils.MultipartBuilder;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.MessageTestUtils.MessageBuilder;
import com.android.email.mail.MessageTestUtils.MultipartBuilder;
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.MimeUtility;
import com.android.email.mail.internet.TextBody;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.ProviderTestUtils;
import com.android.email.provider.EmailContent.Attachment;
import com.android.emailcommon.utility.ConversionUtilities;
import android.content.ContentUris;
import android.content.Context;
@ -191,7 +192,7 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
viewables.add(emptyTextPart);
// a "null" body part of type text/plain should result in a null mTextContent
boolean result = LegacyConversions.updateBodyFields(localBody, localMessage, viewables);
boolean result = ConversionUtilities.updateBodyFields(localBody, localMessage, viewables);
assertTrue(result);
assertNull(localBody.mTextContent);
}

View File

@ -17,17 +17,16 @@
package com.android.email;
import com.android.email.Utility.NewFileCreator;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.ProviderTestUtils;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.ProviderTestUtils;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@ -422,13 +421,13 @@ public class UtilityUnitTests extends AndroidTestCase {
Attachment att = ProviderTestUtils.setupAttachment(mailbox.mId, "name", 123, true,
providerContext);
long attachmentId = att.mId;
Uri uri = AttachmentProvider.getAttachmentUri(account.mId, attachmentId);
Uri uri = AttachmentUtilities.getAttachmentUri(account.mId, attachmentId);
// Case 1: exists in the provider.
assertEquals("name", Utility.getContentFileName(providerContext, uri));
// Case 2: doesn't exist in the provider
Uri notExistUri = AttachmentProvider.getAttachmentUri(account.mId, 123456789);
Uri notExistUri = AttachmentUtilities.getAttachmentUri(account.mId, 123456789);
String lastPathSegment = notExistUri.getLastPathSegment();
assertEquals(lastPathSegment, Utility.getContentFileName(providerContext, notExistUri));
}

View File

@ -21,8 +21,8 @@ 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.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.net.Uri;
@ -63,7 +63,7 @@ public class MessageTestUtils {
* @return AttachmentProvider content URI
*/
public static Uri contentUri(long attachmentId, EmailContent.Account account) {
return AttachmentProvider.getAttachmentUri(account.mId, attachmentId);
return AttachmentUtilities.getAttachmentUri(account.mId, attachmentId);
}
/**

View File

@ -19,11 +19,11 @@ package com.android.email.provider;
import com.android.email.AttachmentInfo;
import com.android.email.R;
import com.android.email.mail.MessagingException;
import com.android.email.provider.AttachmentProvider.AttachmentProviderColumns;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
import com.android.emailcommon.utility.AttachmentUtilities;
import android.content.ContentResolver;
import android.content.Context;
@ -53,7 +53,7 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
ContentResolver mMockResolver;
public AttachmentProviderTests() {
super(AttachmentProvider.class, AttachmentProvider.AUTHORITY);
super(AttachmentProvider.class, AttachmentUtilities.AUTHORITY);
}
@Override
@ -76,9 +76,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
* test insert() - should do nothing
*/
public void testUnimplemented() {
assertEquals(0, mMockResolver.delete(AttachmentProvider.CONTENT_URI, null, null));
assertEquals(0, mMockResolver.update(AttachmentProvider.CONTENT_URI, null, null, null));
assertEquals(null, mMockResolver.insert(AttachmentProvider.CONTENT_URI, null));
assertEquals(0, mMockResolver.delete(AttachmentUtilities.CONTENT_URI, null, null));
assertEquals(0, mMockResolver.update(AttachmentUtilities.CONTENT_URI, null, null,
null));
assertEquals(null, mMockResolver.insert(AttachmentUtilities.CONTENT_URI, null));
}
/**
@ -100,9 +101,12 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
// attachment we add will be id=1 and the 2nd will have id=2. This could fail on
// a legitimate implementation. Asserts below will catch this and fail the test
// if necessary.
Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
Uri attachment2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
Uri attachment3Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
attachment1Id);
Uri attachment2Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
attachment2Id);
Uri attachment3Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
attachment3Id);
// Test with no attachment found - should return null
Cursor c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
@ -113,31 +117,32 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
false, mMockContext);
newAttachment1.mContentUri =
AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id).toString();
AttachmentUtilities.getAttachmentUri(account1.mId, attachment1Id).toString();
attachment1Id = addAttachmentToDb(account1, newAttachment1);
assertEquals("Broken test: Unexpected id assignment", 1, attachment1Id);
Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 200,
false, mMockContext);
newAttachment2.mContentUri =
AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id).toString();
attachment2Id = addAttachmentToDb(account1, newAttachment2);
assertEquals("Broken test: Unexpected id assignment", 2, attachment2Id);
Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1Id, "file3", 300,
false, mMockContext);
newAttachment3.mContentUri =
AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id).toString();
AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id).toString();
attachment3Id = addAttachmentToDb(account1, newAttachment3);
assertEquals("Broken test: Unexpected id assignment", 3, attachment3Id);
// Return a row with all columns specified
attachment2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
attachment2Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
c = mMockResolver.query(
attachment2Uri,
new String[] { AttachmentProviderColumns._ID, AttachmentProviderColumns.DATA,
AttachmentProviderColumns.DISPLAY_NAME,
AttachmentProviderColumns.SIZE },
new String[] { AttachmentUtilities.Columns._ID,
AttachmentUtilities.Columns.DATA,
AttachmentUtilities.Columns.DISPLAY_NAME,
AttachmentUtilities.Columns.SIZE },
null, null, null);
assertEquals(1, c.getCount());
assertTrue(c.moveToFirst());
@ -147,12 +152,13 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
assertEquals(200, c.getInt(3)); // size
// Return a row with permuted columns
attachment3Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
attachment3Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id);
c = mMockResolver.query(
attachment3Uri,
new String[] { AttachmentProviderColumns.SIZE,
AttachmentProviderColumns.DISPLAY_NAME,
AttachmentProviderColumns.DATA, AttachmentProviderColumns._ID },
new String[] { AttachmentUtilities.Columns.SIZE,
AttachmentUtilities.Columns.DISPLAY_NAME,
AttachmentUtilities.Columns.DATA,
AttachmentUtilities.Columns._ID },
null, null, null);
assertEquals(1, c.getCount());
assertTrue(c.moveToFirst());
@ -264,7 +270,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
long attachment5Id = 5;
long attachment6Id = 6;
Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
attachment1Id);
// Test with no attachment found - should return null
String type = mMockResolver.getType(attachment1Uri);
@ -298,27 +305,29 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
attachment6Id = addAttachmentToDb(account1, newAttachment6);
// Check the returned filetypes
Uri uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
Uri uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
type = mMockResolver.getType(uri);
assertEquals("image/jpg", type);
uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id);
type = mMockResolver.getType(uri);
assertEquals("text/plain", type);
uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment4Id);
uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment4Id);
type = mMockResolver.getType(uri);
assertEquals("application/msword", type);
uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment5Id);
uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment5Id);
type = mMockResolver.getType(uri);
assertEquals("application/xyz", type);
uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment6Id);
uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment6Id);
type = mMockResolver.getType(uri);
assertEquals("application/octet-stream", type);
// Check the returned filetypes for the thumbnails
uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment2Id, 62, 62);
uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId, attachment2Id, 62,
62);
type = mMockResolver.getType(uri);
assertEquals("image/png", type);
uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment3Id, 62, 62);
uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId, attachment3Id, 62,
62);
type = mMockResolver.getType(uri);
assertEquals("image/png", type);
}
@ -355,42 +364,49 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
final String FILE_NO_EXT = "myfile";
// .eml files always override mime type
assertEquals("message/rfc822", AttachmentProvider.inferMimeType("a.eml", null));
assertEquals("message/rfc822", AttachmentProvider.inferMimeType("a.eml", ""));
assertEquals("message/rfc822", AttachmentProvider.inferMimeType("a.eml", DEFAULT_LOWER));
assertEquals("message/rfc822", AttachmentProvider.inferMimeType("a.eMl", TEXT_PLAIN));
assertEquals("message/rfc822", AttachmentProvider.inferMimeType("a.eml", TYPE_IMG_PNG));
assertEquals("message/rfc822", AttachmentUtilities.inferMimeType("a.eml", null));
assertEquals("message/rfc822", AttachmentUtilities.inferMimeType("a.eml", ""));
assertEquals("message/rfc822",
AttachmentUtilities.inferMimeType("a.eml", DEFAULT_LOWER));
assertEquals("message/rfc822",
AttachmentUtilities.inferMimeType("a.eMl", TEXT_PLAIN));
assertEquals("message/rfc822",
AttachmentUtilities.inferMimeType("a.eml", TYPE_IMG_PNG));
// Non-generic, non-empty mime type; return it
assertEquals("mime/type", AttachmentProvider.inferMimeType(FILE_PNG, "Mime/TyPe"));
assertEquals("mime/type", AttachmentProvider.inferMimeType(FILE_ABC, "Mime/TyPe"));
assertEquals("mime/type", AttachmentProvider.inferMimeType(FILE_NO_EXT, "Mime/TyPe"));
assertEquals("mime/type", AttachmentProvider.inferMimeType(null, "Mime/TyPe"));
assertEquals("mime/type", AttachmentProvider.inferMimeType("", "Mime/TyPe"));
assertEquals("mime/type", AttachmentUtilities.inferMimeType(FILE_PNG, "Mime/TyPe"));
assertEquals("mime/type", AttachmentUtilities.inferMimeType(FILE_ABC, "Mime/TyPe"));
assertEquals("mime/type",
AttachmentUtilities.inferMimeType(FILE_NO_EXT, "Mime/TyPe"));
assertEquals("mime/type", AttachmentUtilities.inferMimeType(null, "Mime/TyPe"));
assertEquals("mime/type", AttachmentUtilities.inferMimeType("", "Mime/TyPe"));
// Recognizable file extension; return known type
assertEquals("image/png", AttachmentProvider.inferMimeType(FILE_PNG, null));
assertEquals("image/png", AttachmentProvider.inferMimeType(FILE_PNG, ""));
assertEquals("image/png", AttachmentProvider.inferMimeType(FILE_PNG, DEFAULT_MIX));
assertEquals("image/png", AttachmentProvider.inferMimeType(FILE_PNG, TEXT_PLAIN));
assertEquals("image/png", AttachmentUtilities.inferMimeType(FILE_PNG, null));
assertEquals("image/png", AttachmentUtilities.inferMimeType(FILE_PNG, ""));
assertEquals("image/png", AttachmentUtilities.inferMimeType(FILE_PNG, DEFAULT_MIX));
assertEquals("image/png", AttachmentUtilities.inferMimeType(FILE_PNG, TEXT_PLAIN));
// Unrecognized and non-empty file extension, non-"text/plain" type; generate mime type
assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, null));
assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, ""));
assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, DEFAULT_MIX));
assertEquals("application/abc", AttachmentUtilities.inferMimeType(FILE_ABC, null));
assertEquals("application/abc", AttachmentUtilities.inferMimeType(FILE_ABC, ""));
assertEquals("application/abc",
AttachmentUtilities.inferMimeType(FILE_ABC, DEFAULT_MIX));
// Unrecognized and empty file extension, non-"text/plain" type; return "app/octet-stream"
assertEquals(DEFAULT_LOWER, AttachmentProvider.inferMimeType(FILE_NO_EXT, null));
assertEquals(DEFAULT_LOWER, AttachmentProvider.inferMimeType(FILE_NO_EXT, ""));
assertEquals(DEFAULT_LOWER, AttachmentProvider.inferMimeType(FILE_NO_EXT, DEFAULT_MIX));
assertEquals(DEFAULT_LOWER, AttachmentProvider.inferMimeType(null, null));
assertEquals(DEFAULT_LOWER, AttachmentProvider.inferMimeType("", ""));
assertEquals(DEFAULT_LOWER, AttachmentUtilities.inferMimeType(FILE_NO_EXT, null));
assertEquals(DEFAULT_LOWER, AttachmentUtilities.inferMimeType(FILE_NO_EXT, ""));
assertEquals(DEFAULT_LOWER,
AttachmentUtilities.inferMimeType(FILE_NO_EXT, DEFAULT_MIX));
assertEquals(DEFAULT_LOWER, AttachmentUtilities.inferMimeType(null, null));
assertEquals(DEFAULT_LOWER, AttachmentUtilities.inferMimeType("", ""));
// Unrecognized or empty file extension, "text/plain" type; return "text/plain"
assertEquals(TEXT_PLAIN, AttachmentProvider.inferMimeType(FILE_ABC, TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentProvider.inferMimeType(FILE_NO_EXT, TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentProvider.inferMimeType(null, TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentProvider.inferMimeType("", TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentUtilities.inferMimeType(FILE_ABC, TEXT_PLAIN));
assertEquals(TEXT_PLAIN,
AttachmentUtilities.inferMimeType(FILE_NO_EXT, TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentUtilities.inferMimeType(null, TEXT_PLAIN));
assertEquals(TEXT_PLAIN, AttachmentUtilities.inferMimeType("", TEXT_PLAIN));
}
/**
@ -401,17 +417,17 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
final String FILE_EXTENSION = "myfile.pDf";
final String FILE_TWO_EXTENSIONS = "myfile.false.AbC";
assertNull(AttachmentProvider.getFilenameExtension(null));
assertNull(AttachmentProvider.getFilenameExtension(""));
assertNull(AttachmentProvider.getFilenameExtension(FILE_NO_EXTENSION));
assertNull(AttachmentUtilities.getFilenameExtension(null));
assertNull(AttachmentUtilities.getFilenameExtension(""));
assertNull(AttachmentUtilities.getFilenameExtension(FILE_NO_EXTENSION));
assertEquals("pdf", AttachmentProvider.getFilenameExtension(FILE_EXTENSION));
assertEquals("abc", AttachmentProvider.getFilenameExtension(FILE_TWO_EXTENSIONS));
assertEquals("pdf", AttachmentUtilities.getFilenameExtension(FILE_EXTENSION));
assertEquals("abc", AttachmentUtilities.getFilenameExtension(FILE_TWO_EXTENSIONS));
// The API makes no claim as to how these are handled (it probably should),
// but make sure that they don't crash.
AttachmentProvider.getFilenameExtension("filename.");
AttachmentProvider.getFilenameExtension(".extension");
AttachmentUtilities.getFilenameExtension("filename.");
AttachmentUtilities.getFilenameExtension(".extension");
}
/**
@ -431,8 +447,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
// attachment we add will be id=1 and the 2nd will have id=2. This could fail on
// a legitimate implementation. Asserts below will catch this and fail the test
// if necessary.
Uri file1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
Uri file2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
Uri file1Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment1Id);
Uri file2Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
// Test with no attachment found
AssetFileDescriptor afd;
@ -463,7 +479,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
false, mMockContext);
newAttachment2.mContentId = null;
newAttachment2.mContentUri =
AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id)
.toString();
newAttachment2.mMimeType = "image/png";
attachment2Id = addAttachmentToDb(account1, newAttachment2);
assertEquals("Broken test: Unexpected id assignment", 2, attachment2Id);
@ -496,10 +513,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
// attachment we add will be id=1 and the 2nd will have id=2. This could fail on
// a legitimate implementation. Asserts below will catch this and fail the test
// if necessary.
Uri thumb1Uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment1Id,
62, 62);
Uri thumb2Uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment2Id,
62, 62);
Uri thumb1Uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId,
attachment1Id, 62, 62);
Uri thumb2Uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId,
attachment2Id, 62, 62);
// Test with an attached database, but no attachment found
AssetFileDescriptor afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
@ -521,7 +538,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
false, mMockContext);
newAttachment2.mContentId = null;
newAttachment2.mContentUri =
AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id)
.toString();
newAttachment2.mMimeType = "image/png";
attachment2Id = addAttachmentToDb(account1, newAttachment2);
assertEquals("Broken test: Unexpected id assignment", 2, attachment2Id);
@ -539,7 +557,7 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
false, mMockContext);
newAttachment.mContentUri = contentUriStr;
long attachmentId = addAttachmentToDb(account, newAttachment);
Uri attachmentUri = AttachmentProvider.getAttachmentUri(account.mId, attachmentId);
Uri attachmentUri = AttachmentUtilities.getAttachmentUri(account.mId, attachmentId);
return attachmentUri;
}
@ -557,12 +575,13 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
final long message1Id = 1;
// We use attachmentId == 1 but any other id would do
final long attachment1Id = 1;
final Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
final Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
attachment1Id);
// Test with no attachment found - should return input
// We know that the attachmentId 1 does not exist because there are no attachments
// created at this point
Uri result = AttachmentProvider.resolveAttachmentIdToContentUri(
Uri result = AttachmentUtilities.resolveAttachmentIdToContentUri(
mMockResolver, attachment1Uri);
assertEquals(attachment1Uri, result);
@ -571,8 +590,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
// the DB, and does not sample the files, so we won't bother creating the files
{
Uri attachmentUri = createAttachment(account1, message1Id, "file:///path/to/file");
Uri contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(mMockResolver,
attachmentUri);
Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
mMockResolver, attachmentUri);
// When the attachment is found, return the stored content_uri value
assertEquals("file:///path/to/file", contentUri.toString());
}
@ -580,8 +599,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
// Test with existing attachement and contentUri == null
{
Uri attachmentUri = createAttachment(account1, message1Id, null);
Uri contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(mMockResolver,
attachmentUri);
Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
mMockResolver, attachmentUri);
// When contentUri is null should return input
assertEquals(attachmentUri, contentUri);
}
@ -615,25 +634,30 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
createAttachmentFile(account1, newAttachment3.mId);
// Confirm 3 attachment files found
File attachmentsDir = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
File attachmentsDir = AttachmentUtilities.getAttachmentDirectory(mMockContext,
account1.mId);
assertEquals(3, attachmentsDir.listFiles().length);
// Command deletion of some files and check for results
// Message 4 has no attachments so no files should be deleted
AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message4Id);
AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
message4Id);
assertEquals(3, attachmentsDir.listFiles().length);
// Message 3 has no attachment files so no files should be deleted
AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message3Id);
AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
message3Id);
assertEquals(3, attachmentsDir.listFiles().length);
// Message 2 has 2 attachment files so this should delete 2 files
AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message2Id);
AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
message2Id);
assertEquals(1, attachmentsDir.listFiles().length);
// Message 1 has 1 attachment file so this should delete the last file
AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message1Id);
AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
message1Id);
assertEquals(0, attachmentsDir.listFiles().length);
}
@ -655,15 +679,18 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
populateAccountMailbox(account1, mailbox2Id, 1);
// Confirm four attachment files found
File attachmentsDir = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
File attachmentsDir = AttachmentUtilities.getAttachmentDirectory(mMockContext,
account1.mId);
assertEquals(4, attachmentsDir.listFiles().length);
// Command the deletion of mailbox 1 - we should lose 3 attachment files
AttachmentProvider.deleteAllMailboxAttachmentFiles(mMockContext, account1Id, mailbox1Id);
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mMockContext, account1Id,
mailbox1Id);
assertEquals(1, attachmentsDir.listFiles().length);
// Command the deletion of mailbox 2 - we should lose 1 attachment file
AttachmentProvider.deleteAllMailboxAttachmentFiles(mMockContext, account1Id, mailbox2Id);
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mMockContext, account1Id,
mailbox2Id);
assertEquals(0, attachmentsDir.listFiles().length);
}
@ -697,18 +724,20 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
populateAccountMailbox(account2, mailbox4Id, 2);
// Confirm eleven attachment files found
File directory1 = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
File directory1 = AttachmentUtilities.getAttachmentDirectory(mMockContext,
account1.mId);
assertEquals(4, directory1.listFiles().length);
File directory2 = AttachmentProvider.getAttachmentDirectory(mMockContext, account2.mId);
File directory2 = AttachmentUtilities.getAttachmentDirectory(mMockContext,
account2.mId);
assertEquals(7, directory2.listFiles().length);
// Command the deletion of account 1 - we should lose 4 attachment files
AttachmentProvider.deleteAllAccountAttachmentFiles(mMockContext, account1Id);
AttachmentUtilities.deleteAllAccountAttachmentFiles(mMockContext, account1Id);
assertEquals(0, directory1.listFiles().length);
assertEquals(7, directory2.listFiles().length);
// Command the deletion of account 2 - we should lose 7 attachment file
AttachmentProvider.deleteAllAccountAttachmentFiles(mMockContext, account2Id);
AttachmentUtilities.deleteAllAccountAttachmentFiles(mMockContext, account2Id);
assertEquals(0, directory1.listFiles().length);
assertEquals(0, directory2.listFiles().length);
}