New attachment download support for Controller (IMAP/POP3)
* Supports download via AttachmentDownloadService Change-Id: I66143a79b99dcdbd307524ba0b81227f09a00e4a
This commit is contained in:
parent
09fd4d0a18
commit
7894ee82b3
|
@ -276,6 +276,12 @@
|
||||||
>
|
>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".Controller"
|
||||||
|
android:enabled="false"
|
||||||
|
>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.AttachmentDownloadService"
|
android:name=".service.AttachmentDownloadService"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
|
|
|
@ -19,11 +19,13 @@ package com.android.email;
|
||||||
import com.android.email.mail.AuthenticationFailedException;
|
import com.android.email.mail.AuthenticationFailedException;
|
||||||
import com.android.email.mail.MessagingException;
|
import com.android.email.mail.MessagingException;
|
||||||
import com.android.email.mail.Store;
|
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.mail.store.Pop3Store.Pop3Message;
|
||||||
import com.android.email.provider.AttachmentProvider;
|
import com.android.email.provider.AttachmentProvider;
|
||||||
import com.android.email.provider.EmailContent;
|
import com.android.email.provider.EmailContent;
|
||||||
import com.android.email.provider.EmailContent.Account;
|
import com.android.email.provider.EmailContent.Account;
|
||||||
import com.android.email.provider.EmailContent.Attachment;
|
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.Mailbox;
|
||||||
import com.android.email.provider.EmailContent.MailboxColumns;
|
import com.android.email.provider.EmailContent.MailboxColumns;
|
||||||
import com.android.email.provider.EmailContent.Message;
|
import com.android.email.provider.EmailContent.Message;
|
||||||
|
@ -32,17 +34,21 @@ import com.android.email.service.EmailServiceStatus;
|
||||||
import com.android.email.service.IEmailService;
|
import com.android.email.service.IEmailService;
|
||||||
import com.android.email.service.IEmailServiceCallback;
|
import com.android.email.service.IEmailServiceCallback;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -52,10 +58,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
/**
|
/**
|
||||||
* New central controller/dispatcher for Email activities that may require remote operations.
|
* New central controller/dispatcher for Email activities that may require remote operations.
|
||||||
* Handles disambiguating between legacy MessagingController operations and newer provider/sync
|
* Handles disambiguating between legacy MessagingController operations and newer provider/sync
|
||||||
* based code.
|
* based code. We implement Service to allow loadAttachment calls to be sent in a consistent manner
|
||||||
|
* to IMAP, POP3, and EAS by AttachmentDownloadService
|
||||||
*/
|
*/
|
||||||
public class Controller {
|
public class Controller extends Service {
|
||||||
|
private static final String TAG = "Controller";
|
||||||
private static Controller sInstance;
|
private static Controller sInstance;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private Context mProviderContext;
|
private Context mProviderContext;
|
||||||
|
@ -77,22 +84,31 @@ public class Controller {
|
||||||
MailboxColumns.TYPE + "=" + Mailbox.TYPE_ATTACHMENT;
|
MailboxColumns.TYPE + "=" + Mailbox.TYPE_ATTACHMENT;
|
||||||
private static final String WHERE_MAILBOX_KEY = MessageColumns.MAILBOX_KEY + "=?";
|
private static final String WHERE_MAILBOX_KEY = MessageColumns.MAILBOX_KEY + "=?";
|
||||||
|
|
||||||
private static String[] MESSAGEID_TO_ACCOUNTID_PROJECTION = new String[] {
|
private static final String[] MESSAGEID_TO_ACCOUNTID_PROJECTION = new String[] {
|
||||||
EmailContent.RECORD_ID,
|
EmailContent.RECORD_ID,
|
||||||
EmailContent.MessageColumns.ACCOUNT_KEY
|
EmailContent.MessageColumns.ACCOUNT_KEY
|
||||||
};
|
};
|
||||||
private static int MESSAGEID_TO_ACCOUNTID_COLUMN_ACCOUNTID = 1;
|
private static final int MESSAGEID_TO_ACCOUNTID_COLUMN_ACCOUNTID = 1;
|
||||||
|
|
||||||
private static String[] MESSAGEID_TO_MAILBOXID_PROJECTION = new String[] {
|
private static final String[] BODY_SOURCE_KEY_PROJECTION =
|
||||||
|
new String[] {Body.SOURCE_MESSAGE_KEY};
|
||||||
|
private static final int BODY_SOURCE_KEY_COLUMN = 0;
|
||||||
|
private static final String WHERE_MESSAGE_KEY = Body.MESSAGE_KEY + "=?";
|
||||||
|
|
||||||
|
private static final String[] MESSAGEID_TO_MAILBOXID_PROJECTION = new String[] {
|
||||||
EmailContent.RECORD_ID,
|
EmailContent.RECORD_ID,
|
||||||
EmailContent.MessageColumns.MAILBOX_KEY
|
EmailContent.MessageColumns.MAILBOX_KEY
|
||||||
};
|
};
|
||||||
private static int MESSAGEID_TO_MAILBOXID_COLUMN_MAILBOXID = 1;
|
private static final int MESSAGEID_TO_MAILBOXID_COLUMN_MAILBOXID = 1;
|
||||||
|
|
||||||
|
// Service callbacks as set up via setCallback
|
||||||
|
private static RemoteCallbackList<IEmailServiceCallback> sCallbackList =
|
||||||
|
new RemoteCallbackList<IEmailServiceCallback>();
|
||||||
|
|
||||||
protected Controller(Context _context) {
|
protected Controller(Context _context) {
|
||||||
mContext = _context.getApplicationContext();
|
mContext = _context.getApplicationContext();
|
||||||
mProviderContext = _context;
|
mProviderContext = _context;
|
||||||
mLegacyController = MessagingController.getInstance(mProviderContext);
|
mLegacyController = MessagingController.getInstance(mProviderContext, this);
|
||||||
mLegacyController.addListener(mLegacyListener);
|
mLegacyController.addListener(mLegacyListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +122,29 @@ public class Controller {
|
||||||
mLegacyController.removeListener(mLegacyListener);
|
mLegacyController.removeListener(mLegacyListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As a Service, Controller needs this no-argument constructor, but only because there is
|
||||||
|
* another constructor defined for the class. In the typical case for a Service, there are
|
||||||
|
* no defined constructors, so the default constructor is used when the Service is instantiated
|
||||||
|
* by ServiceManager (initialization would be performed in onCreate or onStartCommand, etc.)
|
||||||
|
*
|
||||||
|
* Because of this, the Controller Service, when bound, creates a second instance of the class,
|
||||||
|
* i.e. in addition to the "singleton" created/returned by getInstance. This is unfortunate,
|
||||||
|
* but not disruptive, as the Service Controller instance references members of sInstance (the
|
||||||
|
* previously-singleton Controller); it's perhaps best to think of the Service instance as a
|
||||||
|
* delegate.
|
||||||
|
*
|
||||||
|
* TODO: Have Controller behave more like a real Service. This means that the lifecycle of
|
||||||
|
* the Service (and thus the singleton instance) should be managed by ServiceManager (as happens
|
||||||
|
* with AttachmentDownloadService and MailService), its initialization should be handled in
|
||||||
|
* onCreate(), etc. When this is done (and it should be relatively simple), we will be back
|
||||||
|
* to a true singleton
|
||||||
|
*/
|
||||||
|
public Controller() {
|
||||||
|
mContext = mProviderContext = this;
|
||||||
|
mLegacyController = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or creates the singleton instance of Controller.
|
* Gets or creates the singleton instance of Controller.
|
||||||
*/
|
*/
|
||||||
|
@ -159,12 +198,6 @@ public class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isActiveResultCallback(Result listener) {
|
|
||||||
synchronized (mListeners) {
|
|
||||||
return mListeners.contains(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all Messages that live in the attachment mailbox
|
* Delete all Messages that live in the attachment mailbox
|
||||||
*/
|
*/
|
||||||
|
@ -504,37 +537,28 @@ public class Controller {
|
||||||
Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
|
Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
|
||||||
resolver.update(uri, cv, null, null);
|
resolver.update(uri, cv, null, null);
|
||||||
|
|
||||||
// Split here for target type (Service or MessagingController)
|
sendPendingMessages(accountId);
|
||||||
IEmailService service = getServiceForMessage(messageId);
|
}
|
||||||
if (service != null) {
|
|
||||||
// We just need to be sure the callback is installed, if this is the first call
|
private void sendPendingMessagesSmtp(long accountId) {
|
||||||
// to the service.
|
// for IMAP & POP only, (attempt to) send the message now
|
||||||
try {
|
final EmailContent.Account account =
|
||||||
service.setCallback(mServiceCallback);
|
EmailContent.Account.restoreAccountWithId(mProviderContext, accountId);
|
||||||
} catch (RemoteException re) {
|
if (account == null) {
|
||||||
// OK - not a critical callback here
|
return;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// for IMAP & POP only, (attempt to) send the message now
|
|
||||||
final EmailContent.Account account =
|
|
||||||
EmailContent.Account.restoreAccountWithId(mProviderContext, accountId);
|
|
||||||
if (account == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final long sentboxId = findOrCreateMailboxOfType(accountId, Mailbox.TYPE_SENT);
|
|
||||||
Utility.runAsync(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
mLegacyController.sendPendingMessages(account, sentboxId, mLegacyListener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
final long sentboxId = findOrCreateMailboxOfType(accountId, Mailbox.TYPE_SENT);
|
||||||
|
Utility.runAsync(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mLegacyController.sendPendingMessages(account, sentboxId, mLegacyListener);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to send all pending messages for a given account
|
* Try to send all pending messages for a given account
|
||||||
*
|
*
|
||||||
* @param accountId the account for which to send messages (-1 for all accounts)
|
* @param accountId the account for which to send messages
|
||||||
* @param callback
|
|
||||||
*/
|
*/
|
||||||
public void sendPendingMessages(long accountId) {
|
public void sendPendingMessages(long accountId) {
|
||||||
// 1. make sure we even have an outbox, exit early if not
|
// 1. make sure we even have an outbox, exit early if not
|
||||||
|
@ -557,17 +581,7 @@ public class Controller {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// MessagingController implementation
|
// MessagingController implementation
|
||||||
final EmailContent.Account account =
|
sendPendingMessagesSmtp(accountId);
|
||||||
EmailContent.Account.restoreAccountWithId(mProviderContext, accountId);
|
|
||||||
if (account == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final long sentboxId = findOrCreateMailboxOfType(accountId, Mailbox.TYPE_SENT);
|
|
||||||
Utility.runAsync(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
mLegacyController.sendPendingMessages(account, sentboxId, mLegacyListener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,12 +860,10 @@ public class Controller {
|
||||||
public void loadAttachment(final long attachmentId, final long messageId, final long mailboxId,
|
public void loadAttachment(final long attachmentId, final long messageId, final long mailboxId,
|
||||||
final long accountId) {
|
final long accountId) {
|
||||||
|
|
||||||
File saveToFile = AttachmentProvider.getAttachmentFilename(mProviderContext,
|
|
||||||
accountId, attachmentId);
|
|
||||||
Attachment attachInfo = Attachment.restoreAttachmentWithId(mProviderContext, attachmentId);
|
Attachment attachInfo = Attachment.restoreAttachmentWithId(mProviderContext, attachmentId);
|
||||||
|
if (Utility.attachmentExists(mProviderContext, accountId, attachInfo)) {
|
||||||
if (saveToFile.exists() && attachInfo.mContentUri != null) {
|
|
||||||
// The attachment has already been downloaded, so we will just "pretend" to download it
|
// The attachment has already been downloaded, so we will just "pretend" to download it
|
||||||
|
// This presumably is for POP3 messages
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.loadAttachmentCallback(null, messageId, attachmentId, 0);
|
listener.loadAttachmentCallback(null, messageId, attachmentId, 0);
|
||||||
|
@ -863,27 +875,10 @@ public class Controller {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split here for target type (Service or MessagingController)
|
// Flag the attachment as needing download at the user's request
|
||||||
IEmailService service = getServiceForMessage(messageId);
|
ContentValues cv = new ContentValues();
|
||||||
if (service != null) {
|
cv.put(Attachment.FLAGS, attachInfo.mFlags | Attachment.FLAG_DOWNLOAD_USER_REQUEST);
|
||||||
// Service implementation
|
attachInfo.update(mContext, cv);
|
||||||
try {
|
|
||||||
service.loadAttachment(attachInfo.mId, saveToFile.getAbsolutePath(),
|
|
||||||
AttachmentProvider.getAttachmentUri(accountId, attachmentId).toString());
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// TODO Change exception handling to be consistent with however this method
|
|
||||||
// is implemented for other protocols
|
|
||||||
Log.e("onDownloadAttachment", "RemoteException", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// MessagingController implementation
|
|
||||||
Utility.runAsync(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
mLegacyController.loadAttachment(accountId, messageId, mailboxId, attachmentId,
|
|
||||||
mLegacyListener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1080,7 +1075,13 @@ public class Controller {
|
||||||
* Support for receiving callbacks from MessagingController and dealing with UI going
|
* Support for receiving callbacks from MessagingController and dealing with UI going
|
||||||
* out of scope.
|
* out of scope.
|
||||||
*/
|
*/
|
||||||
private class LegacyListener extends MessagingListener {
|
public class LegacyListener extends MessagingListener implements MessageRetrievalListener {
|
||||||
|
public LegacyListener(long messageId, long attachmentId) {
|
||||||
|
super(messageId, attachmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyListener() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void listFoldersStarted(long accountId) {
|
public void listFoldersStarted(long accountId) {
|
||||||
|
@ -1192,6 +1193,11 @@ public class Controller {
|
||||||
@Override
|
@Override
|
||||||
public void loadAttachmentStarted(long accountId, long messageId, long attachmentId,
|
public void loadAttachmentStarted(long accountId, long messageId, long attachmentId,
|
||||||
boolean requiresDownload) {
|
boolean requiresDownload) {
|
||||||
|
try {
|
||||||
|
mCallbackProxy.loadAttachmentStatus(messageId, attachmentId,
|
||||||
|
EmailServiceStatus.IN_PROGRESS, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.loadAttachmentCallback(null, messageId, attachmentId, 0);
|
listener.loadAttachmentCallback(null, messageId, attachmentId, 0);
|
||||||
|
@ -1201,6 +1207,11 @@ public class Controller {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadAttachmentFinished(long accountId, long messageId, long attachmentId) {
|
public void loadAttachmentFinished(long accountId, long messageId, long attachmentId) {
|
||||||
|
try {
|
||||||
|
mCallbackProxy.loadAttachmentStatus(messageId, attachmentId,
|
||||||
|
EmailServiceStatus.SUCCESS, 100);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.loadAttachmentCallback(null, messageId, attachmentId, 100);
|
listener.loadAttachmentCallback(null, messageId, attachmentId, 100);
|
||||||
|
@ -1209,12 +1220,31 @@ public class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadAttachmentFailed(long accountId, long messageId, long attachmentId,
|
public void loadAttachmentProgress(int progress) {
|
||||||
String reason) {
|
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.loadAttachmentCallback(new MessagingException(reason),
|
listener.loadAttachmentCallback(null, messageId, attachmentId, progress);
|
||||||
messageId, attachmentId, 0);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadAttachmentFailed(long accountId, long messageId, long attachmentId,
|
||||||
|
MessagingException me) {
|
||||||
|
try {
|
||||||
|
// If the cause of the MessagingException is an IOException, we send a status of
|
||||||
|
// CONNECTION_ERROR; in this case, AttachmentDownloadService will try again to
|
||||||
|
// download the attachment. Otherwise, the error is considered non-recoverable.
|
||||||
|
int status = EmailServiceStatus.ATTACHMENT_NOT_FOUND;
|
||||||
|
if (me.getCause() instanceof IOException) {
|
||||||
|
status = EmailServiceStatus.CONNECTION_ERROR;
|
||||||
|
}
|
||||||
|
mCallbackProxy.loadAttachmentStatus(messageId, attachmentId, status, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
synchronized (mListeners) {
|
||||||
|
for (Result listener : mListeners) {
|
||||||
|
listener.loadAttachmentCallback(me, messageId, attachmentId, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1252,6 +1282,10 @@ public class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageRetrieved(com.android.email.mail.Message message) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1294,9 +1328,6 @@ public class Controller {
|
||||||
*/
|
*/
|
||||||
public void sendMessageStatus(long messageId, String subject, int statusCode,
|
public void sendMessageStatus(long messageId, String subject, int statusCode,
|
||||||
int progress) {
|
int progress) {
|
||||||
// Log.d(Email.LOG_TAG, "sendMessageStatus: messageId=" + messageId
|
|
||||||
// + " statusCode=" + statusCode + " progress=" + progress);
|
|
||||||
// Log.d(Email.LOG_TAG, "sendMessageStatus: subject=" + subject);
|
|
||||||
long accountId = -1; // This should be in the callback
|
long accountId = -1; // This should be in the callback
|
||||||
MessagingException result = mapStatusToException(statusCode);
|
MessagingException result = mapStatusToException(statusCode);
|
||||||
switch (statusCode) {
|
switch (statusCode) {
|
||||||
|
@ -1310,8 +1341,6 @@ public class Controller {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Log.d(Email.LOG_TAG, "result=" + result + " messageId=" + messageId
|
|
||||||
// + " progress=" + progress);
|
|
||||||
synchronized(mListeners) {
|
synchronized(mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.sendMailCallback(result, accountId, messageId, progress);
|
listener.sendMailCallback(result, accountId, messageId, progress);
|
||||||
|
@ -1352,9 +1381,7 @@ public class Controller {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// TODO where do we get "number of new messages" as well?
|
|
||||||
// TODO should pass this back instead of looking it up here
|
// TODO should pass this back instead of looking it up here
|
||||||
// TODO smaller projection
|
|
||||||
Mailbox mbx = Mailbox.restoreMailboxWithId(mProviderContext, mailboxId);
|
Mailbox mbx = Mailbox.restoreMailboxWithId(mProviderContext, mailboxId);
|
||||||
// The mailbox could have disappeared if the server commanded it
|
// The mailbox could have disappeared if the server commanded it
|
||||||
if (mbx == null) return;
|
if (mbx == null) return;
|
||||||
|
@ -1393,4 +1420,154 @@ public class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ServiceCallbackWrapper {
|
||||||
|
public void call(IEmailServiceCallback cb) throws RemoteException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy that can be used to broadcast service callbacks; we currently use this only for
|
||||||
|
* loadAttachment callbacks
|
||||||
|
*/
|
||||||
|
private final IEmailServiceCallback.Stub mCallbackProxy =
|
||||||
|
new IEmailServiceCallback.Stub() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast a callback to the everyone that's registered
|
||||||
|
*
|
||||||
|
* @param wrapper the ServiceCallbackWrapper used in the broadcast
|
||||||
|
*/
|
||||||
|
private synchronized void broadcastCallback(ServiceCallbackWrapper wrapper) {
|
||||||
|
if (sCallbackList != null) {
|
||||||
|
// Call everyone on our callback list
|
||||||
|
// Exceptions can be safely ignored
|
||||||
|
int count = sCallbackList.beginBroadcast();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
try {
|
||||||
|
wrapper.call(sCallbackList.getBroadcastItem(i));
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sCallbackList.finishBroadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadAttachmentStatus(final long messageId, final long attachmentId,
|
||||||
|
final int status, final int progress) {
|
||||||
|
broadcastCallback(new ServiceCallbackWrapper() {
|
||||||
|
@Override
|
||||||
|
public void call(IEmailServiceCallback cb) throws RemoteException {
|
||||||
|
cb.loadAttachmentStatus(messageId, attachmentId, status, progress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessageStatus(long messageId, String subject, int statusCode, int progress)
|
||||||
|
throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncMailboxListStatus(long accountId, int statusCode, int progress)
|
||||||
|
throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncMailboxStatus(long mailboxId, int statusCode, int progress)
|
||||||
|
throws RemoteException {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create our EmailService implementation here. For now, only loadAttachment is supported; the
|
||||||
|
* intention, however, is to move more functionality to the service interface
|
||||||
|
*/
|
||||||
|
private final IEmailService.Stub mBinder = new IEmailService.Stub() {
|
||||||
|
|
||||||
|
public Bundle validate(String protocol, String host, String userName, String password,
|
||||||
|
int port, boolean ssl, boolean trustCertificates) throws RemoteException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle autoDiscover(String userName, String password) throws RemoteException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSync(long mailboxId) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopSync(long mailboxId) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadAttachment(long attachmentId, String destinationFile,
|
||||||
|
String contentUriString) throws RemoteException {
|
||||||
|
if (Email.DEBUG) {
|
||||||
|
Log.d(TAG, "loadAttachment: " + attachmentId + " to " + destinationFile);
|
||||||
|
}
|
||||||
|
Attachment att = Attachment.restoreAttachmentWithId(Controller.this, attachmentId);
|
||||||
|
if (att != null) {
|
||||||
|
Message msg = Message.restoreMessageWithId(Controller.this, att.mMessageKey);
|
||||||
|
if (msg != null) {
|
||||||
|
// If the message is a forward and the attachment needs downloading, we need
|
||||||
|
// to retrieve the message from the source, rather than from the message
|
||||||
|
// itself
|
||||||
|
if ((msg.mFlags & Message.FLAG_TYPE_FORWARD) != 0) {
|
||||||
|
String[] cols = Utility.getRowColumns(Controller.this, Body.CONTENT_URI,
|
||||||
|
BODY_SOURCE_KEY_PROJECTION, WHERE_MESSAGE_KEY,
|
||||||
|
new String[] {Long.toString(msg.mId)});
|
||||||
|
if (cols != null) {
|
||||||
|
msg = Message.restoreMessageWithId(Controller.this,
|
||||||
|
Long.parseLong(cols[BODY_SOURCE_KEY_COLUMN]));
|
||||||
|
if (msg == null) {
|
||||||
|
// TODO: We can try restoring from the deleted table at this point...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessagingController legacyController = sInstance.mLegacyController;
|
||||||
|
LegacyListener legacyListener = sInstance.mLegacyListener;
|
||||||
|
legacyController.loadAttachment(msg.mAccountKey, msg.mId, msg.mMailboxKey,
|
||||||
|
attachmentId, legacyListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFolderList(long accountId) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hostChanged(long accountId) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogging(int on) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMeetingResponse(long messageId, int response) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMore(long messageId) throws RemoteException {
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following three methods are not implemented in this version
|
||||||
|
public boolean createFolder(long accountId, String name) throws RemoteException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteFolder(long accountId, String name) throws RemoteException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renameFolder(long accountId, String oldName, String newName)
|
||||||
|
throws RemoteException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(IEmailServiceCallback cb) throws RemoteException {
|
||||||
|
sCallbackList.register(cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return mBinder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package com.android.email;
|
package com.android.email;
|
||||||
|
|
||||||
|
import com.android.email.mail.MessagingException;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -169,6 +171,14 @@ public class GroupMessagingListener extends MessagingListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public void loadAttachmentProgress(
|
||||||
|
int progress) {
|
||||||
|
for (MessagingListener l : mListeners) {
|
||||||
|
l.loadAttachmentProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
synchronized public void loadAttachmentFinished(
|
synchronized public void loadAttachmentFinished(
|
||||||
long accountId,
|
long accountId,
|
||||||
|
@ -184,9 +194,9 @@ public class GroupMessagingListener extends MessagingListener {
|
||||||
long accountId,
|
long accountId,
|
||||||
long messageId,
|
long messageId,
|
||||||
long attachmentId,
|
long attachmentId,
|
||||||
String reason) {
|
MessagingException me) {
|
||||||
for (MessagingListener l : mListeners) {
|
for (MessagingListener l : mListeners) {
|
||||||
l.loadAttachmentFailed(accountId, messageId, attachmentId, reason);
|
l.loadAttachmentFailed(accountId, messageId, attachmentId, me);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ import android.net.Uri;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -122,10 +121,11 @@ public class MessagingController implements Runnable {
|
||||||
private final GroupMessagingListener mListeners = new GroupMessagingListener();
|
private final GroupMessagingListener mListeners = new GroupMessagingListener();
|
||||||
private boolean mBusy;
|
private boolean mBusy;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final Controller mController;
|
||||||
|
|
||||||
protected MessagingController(Context _context) {
|
protected MessagingController(Context _context, Controller _controller) {
|
||||||
mContext = _context.getApplicationContext();
|
mContext = _context.getApplicationContext();
|
||||||
|
mController = _controller;
|
||||||
mThread = new Thread(this);
|
mThread = new Thread(this);
|
||||||
mThread.start();
|
mThread.start();
|
||||||
}
|
}
|
||||||
|
@ -134,9 +134,10 @@ public class MessagingController implements Runnable {
|
||||||
* Gets or creates the singleton instance of MessagingController. Application is used to
|
* Gets or creates the singleton instance of MessagingController. Application is used to
|
||||||
* provide a Context to classes that need it.
|
* provide a Context to classes that need it.
|
||||||
*/
|
*/
|
||||||
public synchronized static MessagingController getInstance(Context _context) {
|
public synchronized static MessagingController getInstance(Context _context,
|
||||||
|
Controller _controller) {
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
sInstance = new MessagingController(_context);
|
sInstance = new MessagingController(_context, _controller);
|
||||||
}
|
}
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
@ -636,6 +637,10 @@ public class MessagingController implements Runnable {
|
||||||
"Error while storing downloaded message." + e.toString());
|
"Error while storing downloaded message." + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadAttachmentProgress(int progress) {
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,6 +773,10 @@ public class MessagingController implements Runnable {
|
||||||
copyOneMessageToProvider(message, account, folder,
|
copyOneMessageToProvider(message, account, folder,
|
||||||
EmailContent.Message.FLAG_LOADED_COMPLETE);
|
EmailContent.Message.FLAG_LOADED_COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadAttachmentProgress(int progress) {
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 14. Download large messages. We ask the server to give us the message structure,
|
// 14. Download large messages. We ask the server to give us the message structure,
|
||||||
|
@ -1816,16 +1825,14 @@ public class MessagingController implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
//1. Check if the attachment is already here and return early in that case
|
//1. Check if the attachment is already here and return early in that case
|
||||||
File saveToFile = AttachmentProvider.getAttachmentFilename(mContext, accountId,
|
|
||||||
attachmentId);
|
|
||||||
Attachment attachment =
|
Attachment attachment =
|
||||||
Attachment.restoreAttachmentWithId(mContext, attachmentId);
|
Attachment.restoreAttachmentWithId(mContext, attachmentId);
|
||||||
if (attachment == null) {
|
if (attachment == null) {
|
||||||
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId,
|
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId,
|
||||||
"Attachment is null");
|
new MessagingException("The attachment is null"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (saveToFile.exists() && attachment.mContentUri != null) {
|
if (Utility.attachmentExists(mContext, accountId, attachment)) {
|
||||||
mListeners.loadAttachmentFinished(accountId, messageId, attachmentId);
|
mListeners.loadAttachmentFinished(accountId, messageId, attachmentId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1841,14 +1848,11 @@ public class MessagingController implements Runnable {
|
||||||
|
|
||||||
if (account == null || mailbox == null || message == null) {
|
if (account == null || mailbox == null || message == null) {
|
||||||
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId,
|
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId,
|
||||||
"Account, mailbox, message or attachment are null");
|
new MessagingException(
|
||||||
|
"Account, mailbox, message or attachment are null"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pruning. Policy is to have one downloaded attachment at a time,
|
|
||||||
// per account, to reduce disk storage pressure.
|
|
||||||
pruneCachedAttachments(accountId);
|
|
||||||
|
|
||||||
Store remoteStore =
|
Store remoteStore =
|
||||||
Store.getInstance(account.getStoreUri(mContext), mContext, null);
|
Store.getInstance(account.getStoreUri(mContext), mContext, null);
|
||||||
Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName);
|
Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName);
|
||||||
|
@ -1879,7 +1883,14 @@ public class MessagingController implements Runnable {
|
||||||
// 4. Now ask for the attachment to be fetched
|
// 4. Now ask for the attachment to be fetched
|
||||||
FetchProfile fp = new FetchProfile();
|
FetchProfile fp = new FetchProfile();
|
||||||
fp.add(storePart);
|
fp.add(storePart);
|
||||||
remoteFolder.fetch(new Message[] { storeMessage }, fp, null);
|
remoteFolder.fetch(new Message[] { storeMessage }, fp,
|
||||||
|
mController.new LegacyListener(messageId, attachmentId));
|
||||||
|
|
||||||
|
// If we failed to load the attachment, throw an Exception here, so that
|
||||||
|
// AttachmentDownloadService knows that we failed
|
||||||
|
if (storePart.getBody() == null) {
|
||||||
|
throw new MessagingException("Attachment not loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Save the downloaded file and update the attachment as necessary
|
// 5. Save the downloaded file and update the attachment as necessary
|
||||||
LegacyConversions.saveAttachmentBody(mContext, storePart, attachment,
|
LegacyConversions.saveAttachmentBody(mContext, storePart, attachment,
|
||||||
|
@ -1890,57 +1901,13 @@ public class MessagingController implements Runnable {
|
||||||
}
|
}
|
||||||
catch (MessagingException me) {
|
catch (MessagingException me) {
|
||||||
if (Email.LOGD) Log.v(Email.LOG_TAG, "", me);
|
if (Email.LOGD) Log.v(Email.LOG_TAG, "", me);
|
||||||
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId,
|
mListeners.loadAttachmentFailed(accountId, messageId, attachmentId, me);
|
||||||
me.getMessage());
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.e(Email.LOG_TAG, "Error while storing attachment." + ioe.toString());
|
Log.e(Email.LOG_TAG, "Error while storing attachment." + ioe.toString());
|
||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Erase all stored attachments for a given account. Rules:
|
|
||||||
* 1. All files in attachment directory are up for deletion
|
|
||||||
* 2. If filename does not match an known attachment id, it's deleted
|
|
||||||
* 3. If the attachment has location data (implying that it's reloadable), it's deleted
|
|
||||||
*/
|
|
||||||
/* package */ void pruneCachedAttachments(long accountId) {
|
|
||||||
ContentResolver resolver = mContext.getContentResolver();
|
|
||||||
File cacheDir = AttachmentProvider.getAttachmentDirectory(mContext, accountId);
|
|
||||||
File[] fileList = cacheDir.listFiles();
|
|
||||||
// fileList can be null if the directory doesn't exist or if there's an IOException
|
|
||||||
if (fileList == null) return;
|
|
||||||
for (File file : fileList) {
|
|
||||||
if (file.exists()) {
|
|
||||||
long id;
|
|
||||||
try {
|
|
||||||
// the name of the file == the attachment id
|
|
||||||
id = Long.valueOf(file.getName());
|
|
||||||
Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, id);
|
|
||||||
Cursor c = resolver.query(uri, PRUNE_ATTACHMENT_PROJECTION, null, null, null);
|
|
||||||
try {
|
|
||||||
if (c.moveToNext()) {
|
|
||||||
// if there is no way to reload the attachment, don't delete it
|
|
||||||
if (c.getString(0) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
// Clear the content URI field since we're losing the attachment
|
|
||||||
resolver.update(uri, PRUNE_ATTACHMENT_CV, null, null);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
// ignore filename != number error, and just delete it anyway
|
|
||||||
}
|
|
||||||
// This file can be safely deleted
|
|
||||||
if (!file.delete()) {
|
|
||||||
file.deleteOnExit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to send any messages that are sitting in the Outbox.
|
* Attempt to send any messages that are sitting in the Outbox.
|
||||||
* @param account
|
* @param account
|
||||||
|
@ -1996,6 +1963,14 @@ public class MessagingController implements Runnable {
|
||||||
try {
|
try {
|
||||||
messageId = c.getLong(0);
|
messageId = c.getLong(0);
|
||||||
mListeners.sendPendingMessagesStarted(account.mId, messageId);
|
mListeners.sendPendingMessagesStarted(account.mId, messageId);
|
||||||
|
// Don't send messages with unloaded attachments
|
||||||
|
if (Utility.hasUnloadedAttachments(mContext, messageId)) {
|
||||||
|
if (Email.DEBUG) {
|
||||||
|
Log.d(Email.LOG_TAG, "Can't send #" + messageId +
|
||||||
|
"; unloaded attachments");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
sender.sendMessage(messageId);
|
sender.sendMessage(messageId);
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
// report error for this message, but keep trying others
|
// report error for this message, but keep trying others
|
||||||
|
@ -2006,6 +1981,15 @@ public class MessagingController implements Runnable {
|
||||||
Uri syncedUri =
|
Uri syncedUri =
|
||||||
ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId);
|
ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId);
|
||||||
if (requireMoveMessageToSentFolder) {
|
if (requireMoveMessageToSentFolder) {
|
||||||
|
// If this is a forwarded message and it has attachments, delete them, as they
|
||||||
|
// duplicate information found elsewhere (on the server). This saves storage.
|
||||||
|
EmailContent.Message msg =
|
||||||
|
EmailContent.Message.restoreMessageWithId(mContext, messageId);
|
||||||
|
if (msg != null &&
|
||||||
|
((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0)) {
|
||||||
|
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId,
|
||||||
|
messageId);
|
||||||
|
}
|
||||||
resolver.update(syncedUri, moveToSentValues, null, null);
|
resolver.update(syncedUri, moveToSentValues, null, null);
|
||||||
} else {
|
} else {
|
||||||
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId, messageId);
|
AttachmentProvider.deleteAllAttachmentFiles(mContext, account.mId, messageId);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package com.android.email;
|
package com.android.email;
|
||||||
|
|
||||||
|
import com.android.email.mail.MessagingException;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,6 +28,17 @@ import android.content.Context;
|
||||||
* changes in this class.
|
* changes in this class.
|
||||||
*/
|
*/
|
||||||
public class MessagingListener {
|
public class MessagingListener {
|
||||||
|
long messageId = -1;
|
||||||
|
long attachmentId = -1;
|
||||||
|
|
||||||
|
public MessagingListener(long _messageId, long _attachmentId) {
|
||||||
|
messageId = _messageId;
|
||||||
|
attachmentId = _attachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessagingListener() {
|
||||||
|
}
|
||||||
|
|
||||||
public void listFoldersStarted(long accountId) {
|
public void listFoldersStarted(long accountId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +94,8 @@ public class MessagingListener {
|
||||||
boolean requiresDownload) {
|
boolean requiresDownload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadAttachmentProgress(int progress) {}
|
||||||
|
|
||||||
public void loadAttachmentFinished(
|
public void loadAttachmentFinished(
|
||||||
long accountId,
|
long accountId,
|
||||||
long messageId,
|
long messageId,
|
||||||
|
@ -91,7 +106,7 @@ public class MessagingListener {
|
||||||
long accountId,
|
long accountId,
|
||||||
long messageId,
|
long messageId,
|
||||||
long attachmentId,
|
long attachmentId,
|
||||||
String reason) {
|
MessagingException me) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,6 +48,7 @@ public abstract class Folder {
|
||||||
*/
|
*/
|
||||||
public interface MessageRetrievalListener {
|
public interface MessageRetrievalListener {
|
||||||
public void messageRetrieved(Message message);
|
public void messageRetrieved(Message message);
|
||||||
|
public void loadAttachmentProgress(int progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.android.email.Preferences;
|
||||||
import com.android.email.Utility;
|
import com.android.email.Utility;
|
||||||
import com.android.email.VendorPolicyLoader;
|
import com.android.email.VendorPolicyLoader;
|
||||||
import com.android.email.mail.AuthenticationFailedException;
|
import com.android.email.mail.AuthenticationFailedException;
|
||||||
|
import com.android.email.mail.Body;
|
||||||
import com.android.email.mail.CertificateValidationException;
|
import com.android.email.mail.CertificateValidationException;
|
||||||
import com.android.email.mail.FetchProfile;
|
import com.android.email.mail.FetchProfile;
|
||||||
import com.android.email.mail.Flag;
|
import com.android.email.mail.Flag;
|
||||||
|
@ -30,6 +31,7 @@ import com.android.email.mail.MessagingException;
|
||||||
import com.android.email.mail.Part;
|
import com.android.email.mail.Part;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
import com.android.email.mail.Transport;
|
import com.android.email.mail.Transport;
|
||||||
|
import com.android.email.mail.internet.BinaryTempFileBody;
|
||||||
import com.android.email.mail.internet.MimeBodyPart;
|
import com.android.email.mail.internet.MimeBodyPart;
|
||||||
import com.android.email.mail.internet.MimeHeader;
|
import com.android.email.mail.internet.MimeHeader;
|
||||||
import com.android.email.mail.internet.MimeMessage;
|
import com.android.email.mail.internet.MimeMessage;
|
||||||
|
@ -59,6 +61,7 @@ import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -98,6 +101,8 @@ public class ImapStore extends Store {
|
||||||
// Always check in FALSE
|
// Always check in FALSE
|
||||||
private static final boolean DEBUG_FORCE_SEND_ID = false;
|
private static final boolean DEBUG_FORCE_SEND_ID = false;
|
||||||
|
|
||||||
|
private static final int COPY_BUFFER_SIZE = 16*1024;
|
||||||
|
|
||||||
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.FLAGGED };
|
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.FLAGGED };
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
@ -995,9 +1000,8 @@ public class ImapStore extends Store {
|
||||||
// decodeBody creates BinaryTempFileBody, but we could avoid this
|
// decodeBody creates BinaryTempFileBody, but we could avoid this
|
||||||
// if we implement ImapStringBody.
|
// if we implement ImapStringBody.
|
||||||
// (We'll need to share a temp file. Protect it with a ref-count.)
|
// (We'll need to share a temp file. Protect it with a ref-count.)
|
||||||
fetchPart.setBody(MimeUtility.decodeBody(
|
fetchPart.setBody(decodeBody(bodyStream, contentTransferEncoding,
|
||||||
bodyStream,
|
fetchPart.getSize(), listener));
|
||||||
contentTransferEncoding));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
|
@ -1012,6 +1016,30 @@ public class ImapStore extends Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any content transfer encoding from the stream and returns a Body.
|
||||||
|
* This code is taken/condensed from MimeUtility.decodeBody
|
||||||
|
*/
|
||||||
|
private Body decodeBody(InputStream in, String contentTransferEncoding, int size,
|
||||||
|
MessageRetrievalListener listener) throws IOException {
|
||||||
|
// Get a properly wrapped input stream
|
||||||
|
in = MimeUtility.getInputStreamForContentTransferEncoding(in, contentTransferEncoding);
|
||||||
|
BinaryTempFileBody tempBody = new BinaryTempFileBody();
|
||||||
|
OutputStream out = tempBody.getOutputStream();
|
||||||
|
byte[] buffer = new byte[COPY_BUFFER_SIZE];
|
||||||
|
int n = 0;
|
||||||
|
int count = 0;
|
||||||
|
while (-1 != (n = in.read(buffer))) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
count += n;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.loadAttachmentProgress(count * 100 / size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
return tempBody;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flag[] getPermanentFlags() throws MessagingException {
|
public Flag[] getPermanentFlags() throws MessagingException {
|
||||||
return PERMANENT_FLAGS;
|
return PERMANENT_FLAGS;
|
||||||
|
|
|
@ -507,8 +507,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
||||||
if (protocol.equals("eas")) {
|
if (protocol.equals("eas")) {
|
||||||
serviceClass = SyncManager.class;
|
serviceClass = SyncManager.class;
|
||||||
} else {
|
} else {
|
||||||
// Uncomment this when Controller has been made a Service
|
serviceClass = com.android.email.Controller.class;
|
||||||
//serviceClass = Controller.class;
|
|
||||||
}
|
}
|
||||||
mAccountServiceMap.put(accountId, serviceClass);
|
mAccountServiceMap.put(accountId, serviceClass);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue