AI 147730: 1. Create an API by which a Store can specify its own custom

synchronizer code.
  2. Refactor (and spell-fix) the core folder synchronizer.  Extract
  the innards that are IMAP/POP specific, leaving common wrapper
  code in a simpler shell.
  3. For each account & folder to sync, check the store and call
  the specialized sync'er (if provided) or the generic one.
  BUG=1807499

Automated import of CL 147730
This commit is contained in:
Andy Stadler 2009-04-24 13:25:34 -07:00 committed by The Android Open Source Project
parent 5551f7feb2
commit 100867a231
4 changed files with 477 additions and 366 deletions

View File

@ -25,6 +25,7 @@ import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.Sender;
import com.android.email.mail.Store;
import com.android.email.mail.StoreSynchronizer;
import com.android.email.mail.Folder.FolderType;
import com.android.email.mail.Folder.OpenMode;
import com.android.email.mail.internet.MimeUtility;
@ -381,9 +382,6 @@ public class MessagingController implements Runnable {
* Start background synchronization of the specified folder.
* @param account
* @param folder
* @param numNewestMessagesToKeep Specifies the number of messages that should be
* considered as part of the window of available messages. This number effectively limits
* the user's view into the mailbox to the newest (numNewestMessagesToKeep) messages.
* @param listener
*/
public void synchronizeMailbox(final Account account, final String folder,
@ -399,393 +397,46 @@ public class MessagingController implements Runnable {
}
put("synchronizeMailbox", listener, new Runnable() {
public void run() {
synchronizeMailboxSyncronous(account, folder);
synchronizeMailboxSynchronous(account, folder);
}
});
}
/**
* Start foreground synchronization of the specified folder. This is generally only called
* by synchronizeMailbox.
* Start foreground synchronization of the specified folder. This is called by
* synchronizeMailbox or checkMail.
* @param account
* @param folder
* @param numNewestMessagesToKeep Specifies the number of messages that should be
* considered as part of the window of available messages. This number effectively limits
* the user's view into the mailbox to the newest (numNewestMessagesToKeep) messages.
* @param listener
*
* TODO Break this method up into smaller chunks.
*/
public void synchronizeMailboxSyncronous(final Account account, final String folder) {
private void synchronizeMailboxSynchronous(final Account account, final String folder) {
for (MessagingListener l : mListeners) {
l.synchronizeMailboxStarted(account, folder);
}
try {
processPendingCommandsSynchronous(account);
/*
* Get the message list from the local store and create an index of
* the uids within the list.
*/
final LocalStore localStore =
(LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication, null);
final LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE, null);
Message[] localMessages = localFolder.getMessages(null);
HashMap<String, Message> localUidMap = new HashMap<String, Message>();
for (Message message : localMessages) {
localUidMap.put(message.getUid(), message);
}
StoreSynchronizer.SyncResults results;
// Select generic sync or store-specific sync
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication,
account.getStoreCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
/*
* If the folder is a "special" folder we need to see if it exists
* on the remote server. It if does not exist we'll try to create it. If we
* can't create we'll abort. This will happen on every single Pop3 folder as
* designed and on Imap folders during error conditions. This allows us
* to treat Pop3 and Imap the same in this code.
*/
if (folder.equals(account.getTrashFolderName()) ||
folder.equals(account.getSentFolderName()) ||
folder.equals(account.getDraftsFolderName())) {
if (!remoteFolder.exists()) {
if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
for (MessagingListener l : mListeners) {
l.synchronizeMailboxFinished(account, folder, 0, 0);
}
return;
}
}
StoreSynchronizer customSync = remoteStore.getMessageSynchronizer();
if (customSync == null) {
results = synchronizeMailboxGeneric(account, folder);
} else {
results = customSync.SynchronizeMessagesSynchronous(
account, folder, mListeners, mApplication);
}
/*
* Synchronization process:
Open the folder
Upload any local messages that are marked as PENDING_UPLOAD (Drafts, Sent, Trash)
Get the message count
Get the list of the newest Email.DEFAULT_VISIBLE_LIMIT messages
getMessages(messageCount - Email.DEFAULT_VISIBLE_LIMIT, messageCount)
See if we have each message locally, if not fetch it's flags and envelope
Get and update the unread count for the folder
Update the remote flags of any messages we have locally with an internal date
newer than the remote message.
Get the current flags for any messages we have locally but did not just download
Update local flags
For any message we have locally but not remotely, delete the local message to keep
cache clean.
Download larger parts of any new messages.
(Optional) Download small attachments in the background.
*/
/*
* Open the remote folder. This pre-loads certain metadata like message count.
*/
remoteFolder.open(OpenMode.READ_WRITE, localFolder.getPersistentCallbacks());
/*
* Trash any remote messages that are marked as trashed locally.
*/
/*
* Get the remote message count.
*/
int remoteMessageCount = remoteFolder.getMessageCount();
int visibleLimit = localFolder.getVisibleLimit();
if (visibleLimit <= 0) {
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(account.getStoreUri(),
mApplication);
visibleLimit = info.mVisibleLimitDefault;
localFolder.setVisibleLimit(visibleLimit);
}
Message[] remoteMessages = new Message[0];
final ArrayList<Message> unsyncedMessages = new ArrayList<Message>();
HashMap<String, Message> remoteUidMap = new HashMap<String, Message>();
if (remoteMessageCount > 0) {
/*
* Message numbers start at 1.
*/
int remoteStart = Math.max(0, remoteMessageCount - visibleLimit) + 1;
int remoteEnd = remoteMessageCount;
remoteMessages = remoteFolder.getMessages(remoteStart, remoteEnd, null);
for (Message message : remoteMessages) {
remoteUidMap.put(message.getUid(), message);
}
/*
* Get a list of the messages that are in the remote list but not on the
* local store, or messages that are in the local store but failed to download
* on the last sync. These are the new messages that we will download.
*/
for (Message message : remoteMessages) {
Message localMessage = localUidMap.get(message.getUid());
if (localMessage == null ||
(!localMessage.isSet(Flag.X_DOWNLOADED_FULL) &&
!localMessage.isSet(Flag.X_DOWNLOADED_PARTIAL))) {
unsyncedMessages.add(message);
}
}
}
/*
* A list of messages that were downloaded and which did not have the Seen flag set.
* This will serve to indicate the true "new" message count that will be reported to
* the user via notification.
*/
final ArrayList<Message> newMessages = new ArrayList<Message>();
/*
* Fetch the flags and envelope only of the new messages. This is intended to get us
* critical data as fast as possible, and then we'll fill in the details.
*/
if (unsyncedMessages.size() > 0) {
/*
* Reverse the order of the messages. Depending on the server this may get us
* fetch results for newest to oldest. If not, no harm done.
*/
Collections.reverse(unsyncedMessages);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.FLAGS);
fp.add(FetchProfile.Item.ENVELOPE);
remoteFolder.fetch(unsyncedMessages.toArray(new Message[0]), fp,
new MessageRetrievalListener() {
public void messageFinished(Message message, int number, int ofTotal) {
try {
// Store the new message locally
localFolder.appendMessages(new Message[] {
message
});
// And include it in the view
if (message.getSubject() != null &&
message.getFrom() != null) {
/*
* We check to make sure that we got something worth
* showing (subject and from) because some protocols
* (POP) may not be able to give us headers for
* ENVELOPE, only size.
*/
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(account, folder,
localFolder.getMessage(message.getUid()));
}
}
if (!message.isSet(Flag.SEEN)) {
newMessages.add(message);
}
}
catch (Exception e) {
Log.e(Email.LOG_TAG,
"Error while storing downloaded message.",
e);
}
}
public void messageStarted(String uid, int number, int ofTotal) {
}
});
}
/*
* Refresh the flags for any messages in the local store that we didn't just
* download.
*/
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.FLAGS);
remoteFolder.fetch(remoteMessages, fp, null);
for (Message remoteMessage : remoteMessages) {
Message localMessage = localFolder.getMessage(remoteMessage.getUid());
if (localMessage == null) {
continue;
}
if (remoteMessage.isSet(Flag.SEEN) != localMessage.isSet(Flag.SEEN)) {
localMessage.setFlag(Flag.SEEN, remoteMessage.isSet(Flag.SEEN));
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(account, folder, localMessage);
}
}
}
/*
* Get and store the unread message count.
*/
int remoteUnreadMessageCount = remoteFolder.getUnreadMessageCount();
if (remoteUnreadMessageCount == -1) {
localFolder.setUnreadMessageCount(localFolder.getUnreadMessageCount()
+ newMessages.size());
}
else {
localFolder.setUnreadMessageCount(remoteUnreadMessageCount);
}
/*
* Remove any messages that are in the local store but no longer on the remote store.
*/
for (Message localMessage : localMessages) {
if (remoteUidMap.get(localMessage.getUid()) == null) {
localMessage.setFlag(Flag.X_DESTROYED, true);
for (MessagingListener l : mListeners) {
l.synchronizeMailboxRemovedMessage(account, folder, localMessage);
}
}
}
/*
* Now we download the actual content of messages.
*/
ArrayList<Message> largeMessages = new ArrayList<Message>();
ArrayList<Message> smallMessages = new ArrayList<Message>();
for (Message message : unsyncedMessages) {
/*
* Sort the messages into two buckets, small and large. Small messages will be
* downloaded fully and large messages will be downloaded in parts. By sorting
* into two buckets we can pipeline the commands for each set of messages
* into a single command to the server saving lots of round trips.
*/
if (message.getSize() > (MAX_SMALL_MESSAGE_SIZE)) {
largeMessages.add(message);
} else {
smallMessages.add(message);
}
}
/*
* Grab the content of the small messages first. This is going to
* be very fast and at very worst will be a single up of a few bytes and a single
* download of 625k.
*/
fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
remoteFolder.fetch(smallMessages.toArray(new Message[smallMessages.size()]),
fp, new MessageRetrievalListener() {
public void messageFinished(Message message, int number, int ofTotal) {
try {
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating this message has now be fully downloaded
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
// Update the listener with what we've found
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(
account,
folder,
localMessage);
}
}
catch (MessagingException me) {
}
}
public void messageStarted(String uid, int number, int ofTotal) {
}
});
/*
* Now do the large messages that require more round trips.
*/
fp.clear();
fp.add(FetchProfile.Item.STRUCTURE);
remoteFolder.fetch(largeMessages.toArray(new Message[largeMessages.size()]),
fp, null);
for (Message message : largeMessages) {
if (message.getBody() == null) {
/*
* The provider was unable to get the structure of the message, so
* we'll download a reasonable portion of the messge and mark it as
* incomplete so the entire thing can be downloaded later if the user
* wishes to download it.
*/
fp.clear();
fp.add(FetchProfile.Item.BODY_SANE);
/*
* TODO a good optimization here would be to make sure that all Stores set
* the proper size after this fetch and compare the before and after size. If
* they equal we can mark this SYNCHRONIZED instead of PARTIALLY_SYNCHRONIZED
*/
remoteFolder.fetch(new Message[] { message }, fp, null);
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating that the message has been partially downloaded and
// is ready for view.
localMessage.setFlag(Flag.X_DOWNLOADED_PARTIAL, true);
} else {
/*
* We have a structure to deal with, from which
* we can pull down the parts we want to actually store.
* Build a list of parts we are interested in. Text parts will be downloaded
* right now, attachments will be left for later.
*/
ArrayList<Part> viewables = new ArrayList<Part>();
ArrayList<Part> attachments = new ArrayList<Part>();
MimeUtility.collectParts(message, viewables, attachments);
/*
* Now download the parts we're interested in storing.
*/
for (Part part : viewables) {
fp.clear();
fp.add(part);
// TODO what happens if the network connection dies? We've got partial
// messages with incorrect status stored.
remoteFolder.fetch(new Message[] { message }, fp, null);
}
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating this message has been fully downloaded and can be
// viewed.
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
// Update the listener with what we've found
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(
account,
folder,
localFolder.getMessage(message.getUid()));
}
}
/*
* Notify listeners that we're finally done.
*/
for (MessagingListener l : mListeners) {
l.synchronizeMailboxFinished(
account,
folder,
remoteFolder.getMessageCount(), newMessages.size());
results.mTotalMessages, results.mNewMessages);
}
remoteFolder.close(false);
localFolder.close(false);
}
catch (Exception e) {
} catch (Exception e) {
if (Config.LOGV) {
Log.v(Email.LOG_TAG, "synchronizeMailbox", e);
}
@ -797,6 +448,376 @@ public class MessagingController implements Runnable {
}
}
}
/**
* Generic synchronizer - used for POP3 and IMAP.
*
* TODO Break this method up into smaller chunks.
*
* @param account
* @param folder
* @return
* @throws MessagingException
*/
private StoreSynchronizer.SyncResults synchronizeMailboxGeneric(final Account account,
final String folder) throws MessagingException {
/*
* Get the message list from the local store and create an index of
* the uids within the list.
*/
final LocalStore localStore =
(LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication, null);
final LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE, null);
Message[] localMessages = localFolder.getMessages(null);
HashMap<String, Message> localUidMap = new HashMap<String, Message>();
for (Message message : localMessages) {
localUidMap.put(message.getUid(), message);
}
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication,
account.getStoreCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
/*
* If the folder is a "special" folder we need to see if it exists
* on the remote server. It if does not exist we'll try to create it. If we
* can't create we'll abort. This will happen on every single Pop3 folder as
* designed and on Imap folders during error conditions. This allows us
* to treat Pop3 and Imap the same in this code.
*/
if (folder.equals(account.getTrashFolderName()) ||
folder.equals(account.getSentFolderName()) ||
folder.equals(account.getDraftsFolderName())) {
if (!remoteFolder.exists()) {
if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
return new StoreSynchronizer.SyncResults(0, 0);
}
}
}
/*
* Synchronization process:
Open the folder
Upload any local messages that are marked as PENDING_UPLOAD (Drafts, Sent, Trash)
Get the message count
Get the list of the newest Email.DEFAULT_VISIBLE_LIMIT messages
getMessages(messageCount - Email.DEFAULT_VISIBLE_LIMIT, messageCount)
See if we have each message locally, if not fetch it's flags and envelope
Get and update the unread count for the folder
Update the remote flags of any messages we have locally with an internal date
newer than the remote message.
Get the current flags for any messages we have locally but did not just download
Update local flags
For any message we have locally but not remotely, delete the local message to keep
cache clean.
Download larger parts of any new messages.
(Optional) Download small attachments in the background.
*/
/*
* Open the remote folder. This pre-loads certain metadata like message count.
*/
remoteFolder.open(OpenMode.READ_WRITE, localFolder.getPersistentCallbacks());
/*
* Trash any remote messages that are marked as trashed locally.
*/
/*
* Get the remote message count.
*/
int remoteMessageCount = remoteFolder.getMessageCount();
int visibleLimit = localFolder.getVisibleLimit();
if (visibleLimit <= 0) {
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(account.getStoreUri(),
mApplication);
visibleLimit = info.mVisibleLimitDefault;
localFolder.setVisibleLimit(visibleLimit);
}
Message[] remoteMessages = new Message[0];
final ArrayList<Message> unsyncedMessages = new ArrayList<Message>();
HashMap<String, Message> remoteUidMap = new HashMap<String, Message>();
if (remoteMessageCount > 0) {
/*
* Message numbers start at 1.
*/
int remoteStart = Math.max(0, remoteMessageCount - visibleLimit) + 1;
int remoteEnd = remoteMessageCount;
remoteMessages = remoteFolder.getMessages(remoteStart, remoteEnd, null);
for (Message message : remoteMessages) {
remoteUidMap.put(message.getUid(), message);
}
/*
* Get a list of the messages that are in the remote list but not on the
* local store, or messages that are in the local store but failed to download
* on the last sync. These are the new messages that we will download.
*/
for (Message message : remoteMessages) {
Message localMessage = localUidMap.get(message.getUid());
if (localMessage == null ||
(!localMessage.isSet(Flag.X_DOWNLOADED_FULL) &&
!localMessage.isSet(Flag.X_DOWNLOADED_PARTIAL))) {
unsyncedMessages.add(message);
}
}
}
/*
* A list of messages that were downloaded and which did not have the Seen flag set.
* This will serve to indicate the true "new" message count that will be reported to
* the user via notification.
*/
final ArrayList<Message> newMessages = new ArrayList<Message>();
/*
* Fetch the flags and envelope only of the new messages. This is intended to get us
* critical data as fast as possible, and then we'll fill in the details.
*/
if (unsyncedMessages.size() > 0) {
/*
* Reverse the order of the messages. Depending on the server this may get us
* fetch results for newest to oldest. If not, no harm done.
*/
Collections.reverse(unsyncedMessages);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.FLAGS);
fp.add(FetchProfile.Item.ENVELOPE);
remoteFolder.fetch(unsyncedMessages.toArray(new Message[0]), fp,
new MessageRetrievalListener() {
public void messageFinished(Message message, int number, int ofTotal) {
try {
// Store the new message locally
localFolder.appendMessages(new Message[] {
message
});
// And include it in the view
if (message.getSubject() != null &&
message.getFrom() != null) {
/*
* We check to make sure that we got something worth
* showing (subject and from) because some protocols
* (POP) may not be able to give us headers for
* ENVELOPE, only size.
*/
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(account, folder,
localFolder.getMessage(message.getUid()));
}
}
if (!message.isSet(Flag.SEEN)) {
newMessages.add(message);
}
}
catch (Exception e) {
Log.e(Email.LOG_TAG,
"Error while storing downloaded message.",
e);
}
}
public void messageStarted(String uid, int number, int ofTotal) {
}
});
}
/*
* Refresh the flags for any messages in the local store that we didn't just
* download.
*/
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.FLAGS);
remoteFolder.fetch(remoteMessages, fp, null);
for (Message remoteMessage : remoteMessages) {
Message localMessage = localFolder.getMessage(remoteMessage.getUid());
if (localMessage == null) {
continue;
}
if (remoteMessage.isSet(Flag.SEEN) != localMessage.isSet(Flag.SEEN)) {
localMessage.setFlag(Flag.SEEN, remoteMessage.isSet(Flag.SEEN));
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(account, folder, localMessage);
}
}
}
/*
* Get and store the unread message count.
*/
int remoteUnreadMessageCount = remoteFolder.getUnreadMessageCount();
if (remoteUnreadMessageCount == -1) {
localFolder.setUnreadMessageCount(localFolder.getUnreadMessageCount()
+ newMessages.size());
}
else {
localFolder.setUnreadMessageCount(remoteUnreadMessageCount);
}
/*
* Remove any messages that are in the local store but no longer on the remote store.
*/
for (Message localMessage : localMessages) {
if (remoteUidMap.get(localMessage.getUid()) == null) {
localMessage.setFlag(Flag.X_DESTROYED, true);
for (MessagingListener l : mListeners) {
l.synchronizeMailboxRemovedMessage(account, folder, localMessage);
}
}
}
/*
* Now we download the actual content of messages.
*/
ArrayList<Message> largeMessages = new ArrayList<Message>();
ArrayList<Message> smallMessages = new ArrayList<Message>();
for (Message message : unsyncedMessages) {
/*
* Sort the messages into two buckets, small and large. Small messages will be
* downloaded fully and large messages will be downloaded in parts. By sorting
* into two buckets we can pipeline the commands for each set of messages
* into a single command to the server saving lots of round trips.
*/
if (message.getSize() > (MAX_SMALL_MESSAGE_SIZE)) {
largeMessages.add(message);
} else {
smallMessages.add(message);
}
}
/*
* Grab the content of the small messages first. This is going to
* be very fast and at very worst will be a single up of a few bytes and a single
* download of 625k.
*/
fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
remoteFolder.fetch(smallMessages.toArray(new Message[smallMessages.size()]),
fp, new MessageRetrievalListener() {
public void messageFinished(Message message, int number, int ofTotal) {
try {
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating this message has now be fully downloaded
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
// Update the listener with what we've found
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(
account,
folder,
localMessage);
}
}
catch (MessagingException me) {
}
}
public void messageStarted(String uid, int number, int ofTotal) {
}
});
/*
* Now do the large messages that require more round trips.
*/
fp.clear();
fp.add(FetchProfile.Item.STRUCTURE);
remoteFolder.fetch(largeMessages.toArray(new Message[largeMessages.size()]),
fp, null);
for (Message message : largeMessages) {
if (message.getBody() == null) {
/*
* The provider was unable to get the structure of the message, so
* we'll download a reasonable portion of the messge and mark it as
* incomplete so the entire thing can be downloaded later if the user
* wishes to download it.
*/
fp.clear();
fp.add(FetchProfile.Item.BODY_SANE);
/*
* TODO a good optimization here would be to make sure that all Stores set
* the proper size after this fetch and compare the before and after size. If
* they equal we can mark this SYNCHRONIZED instead of PARTIALLY_SYNCHRONIZED
*/
remoteFolder.fetch(new Message[] { message }, fp, null);
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating that the message has been partially downloaded and
// is ready for view.
localMessage.setFlag(Flag.X_DOWNLOADED_PARTIAL, true);
} else {
/*
* We have a structure to deal with, from which
* we can pull down the parts we want to actually store.
* Build a list of parts we are interested in. Text parts will be downloaded
* right now, attachments will be left for later.
*/
ArrayList<Part> viewables = new ArrayList<Part>();
ArrayList<Part> attachments = new ArrayList<Part>();
MimeUtility.collectParts(message, viewables, attachments);
/*
* Now download the parts we're interested in storing.
*/
for (Part part : viewables) {
fp.clear();
fp.add(part);
// TODO what happens if the network connection dies? We've got partial
// messages with incorrect status stored.
remoteFolder.fetch(new Message[] { message }, fp, null);
}
// Store the updated message locally
localFolder.appendMessages(new Message[] {
message
});
Message localMessage = localFolder.getMessage(message.getUid());
// Set a flag indicating this message has been fully downloaded and can be
// viewed.
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
// Update the listener with what we've found
for (MessagingListener l : mListeners) {
l.synchronizeMailboxNewMessage(
account,
folder,
localFolder.getMessage(message.getUid()));
}
}
/*
* Report successful sync
*/
StoreSynchronizer.SyncResults results = new StoreSynchronizer.SyncResults(
remoteFolder.getMessageCount(), newMessages.size());
remoteFolder.close(false);
localFolder.close(false);
return results;
}
private void queuePendingCommand(Account account, PendingCommand command) {
try {
@ -1509,7 +1530,7 @@ public class MessagingController implements Runnable {
}
for (Account account : accounts) {
sendPendingMessagesSynchronous(account);
synchronizeMailboxSyncronous(account, Email.INBOX);
synchronizeMailboxSynchronous(account, Email.INBOX);
}
for (MessagingListener l : mListeners) {
l.checkMailFinished(context, null); // TODO this needs to pass the actual array

View File

@ -189,6 +189,14 @@ public abstract class Store {
return com.android.email.activity.setup.AccountSetupIncoming.class;
}
/**
* Get class of sync'er for this Store class
* @return Message Sync controller, or null to use default
*/
public StoreSynchronizer getMessageSynchronizer() {
return null;
}
public abstract Folder getFolder(String name) throws MessagingException;
public abstract Folder[] getPersonalNamespaces() throws MessagingException;

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.mail;
import com.android.email.Account;
import com.android.email.MessagingListener;
import android.content.Context;
import java.util.Collection;
/**
* This interface allows a store to define a completely different synchronizer algorithm,
* as necessary.
*/
public interface StoreSynchronizer {
/**
* An object of this class is returned by SynchronizeMessagesSynchronous to report
* the results of the sync run.
*/
public static class SyncResults {
/**
* The total # of messages in the folder
*/
public int mTotalMessages;
/**
* The # of new messages in the folder
*/
public int mNewMessages;
public SyncResults(int totalMessages, int newMessages) {
mTotalMessages = totalMessages;
mNewMessages = newMessages;
}
}
/**
* The job of this method is to synchronize messages between a remote folder and the
* corresponding local folder.
*
* The following callbacks should be called during this operation:
* {@link MessagingListener#synchronizeMailboxNewMessage(Account, String, Message)}
* {@link MessagingListener#synchronizeMailboxRemovedMessage(Account, String, Message)}
*
* @param account The account to synchronize
* @param folder The folder to synchronize
* @param listeners callbacks to make during sync operation
* @param context if needed for making system calls
* @return an object describing the sync results
*/
public SyncResults SynchronizeMessagesSynchronous(Account account, String folder,
Collection<MessagingListener> listeners, Context context) throws MessagingException;
}

View File

@ -20,6 +20,7 @@ import com.android.email.Email;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
import com.android.email.mail.StoreSynchronizer;
import android.content.Context;
import android.util.Config;
@ -151,5 +152,17 @@ public class ExchangeStoreExample extends Store {
public Class<? extends android.app.Activity> getSettingActivityClass() {
return com.android.email.activity.setup.AccountSetupExchange.class;
}
/**
* Get class of sync'er for this Store class. Because exchange Sync rules are so different
* than IMAP or POP3, it's likely that an Exchange implementation will need its own sync
* controller. If so, this function must return a non-null value.
*
* @return Message Sync controller, or null to use default
*/
@Override
public StoreSynchronizer getMessageSynchronizer() {
return null;
}
}