mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-24 02:12:15 -05:00
Major IMAP IDLE rework:
1) Actual message sync on MessagingController uses same connection as IDLE for faster and more efficient push operation. Uses fewer connections to the server. 2) More aggressive handling of untagged responses should more reliably get flag changes and new messages when many events happen at once. 3) Simplification of new mail notification 4) Push mail now respects the folder visible limit 5) When multiple untagged FETCH responses arrive en bloc, the actual message flags and UIDs are fetched with a single request
This commit is contained in:
parent
e4427f4f17
commit
fc91603429
@ -52,6 +52,7 @@ import com.android.email.mail.FetchProfile;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessageRemovalListener;
|
||||
import com.android.email.mail.MessageRetrievalListener;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Part;
|
||||
@ -1061,7 +1062,7 @@ public class MessagingController implements Runnable
|
||||
/*
|
||||
* Now we download the actual content of messages.
|
||||
*/
|
||||
int newMessages = downloadMessages(account, remoteFolder, localFolder, remoteMessages);
|
||||
int newMessages = downloadMessages(account, remoteFolder, localFolder, remoteMessages, false);
|
||||
|
||||
setLocalUnreadCountToRemote(localFolder, remoteFolder, newMessages);
|
||||
|
||||
@ -1163,22 +1164,14 @@ public class MessagingController implements Runnable
|
||||
}
|
||||
|
||||
private int downloadMessages(final Account account, final Folder remoteFolder,
|
||||
final LocalFolder localFolder, List<Message> inputMessages) throws MessagingException
|
||||
final LocalFolder localFolder, List<Message> inputMessages, boolean flagSyncOnly) throws MessagingException
|
||||
{
|
||||
final String folder = remoteFolder.getName();
|
||||
|
||||
ArrayList<Message> syncFlagMessages = new ArrayList<Message>();
|
||||
ArrayList<Message> unsyncedMessages = new ArrayList<Message>();
|
||||
List<Message> unsyncedMessages = new ArrayList<Message>();
|
||||
final AtomicInteger newMessages = new AtomicInteger(0);
|
||||
|
||||
int visibleLimit = localFolder.getVisibleLimit();
|
||||
int listSize = inputMessages.size();
|
||||
|
||||
if (listSize > visibleLimit)
|
||||
{
|
||||
inputMessages = inputMessages.subList(listSize - visibleLimit, listSize);
|
||||
}
|
||||
|
||||
List<Message> messages = new ArrayList<Message>(inputMessages);
|
||||
|
||||
for (Message message : messages)
|
||||
@ -1189,35 +1182,38 @@ public class MessagingController implements Runnable
|
||||
|
||||
if (localMessage == null)
|
||||
{
|
||||
if (!message.isSet(Flag.X_DOWNLOADED_FULL) && !message.isSet(Flag.X_DOWNLOADED_PARTIAL))
|
||||
if (!flagSyncOnly)
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
if (!message.isSet(Flag.X_DOWNLOADED_FULL) && !message.isSet(Flag.X_DOWNLOADED_PARTIAL))
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Message with uid " + message.getUid() + " is not downloaded at all");
|
||||
}
|
||||
|
||||
unsyncedMessages.add(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Message with uid " + message.getUid() + " is partially or fully downloaded");
|
||||
}
|
||||
// Store the updated message locally
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
|
||||
localMessage = localFolder.getMessage(message.getUid());
|
||||
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, message.isSet(Flag.X_DOWNLOADED_FULL));
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_PARTIAL, message.isSet(Flag.X_DOWNLOADED_PARTIAL));
|
||||
|
||||
for (MessagingListener l : getListeners())
|
||||
{
|
||||
l.synchronizeMailboxAddOrUpdateMessage(account, folder, localMessage);
|
||||
if (!localMessage.isSet(Flag.SEEN))
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
l.synchronizeMailboxNewMessage(account, folder, localMessage);
|
||||
Log.v(Email.LOG_TAG, "Message with uid " + message.getUid() + " is not downloaded at all");
|
||||
}
|
||||
|
||||
unsyncedMessages.add(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Message with uid " + message.getUid() + " is partially or fully downloaded");
|
||||
}
|
||||
// Store the updated message locally
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
|
||||
localMessage = localFolder.getMessage(message.getUid());
|
||||
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, message.isSet(Flag.X_DOWNLOADED_FULL));
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_PARTIAL, message.isSet(Flag.X_DOWNLOADED_PARTIAL));
|
||||
|
||||
for (MessagingListener l : getListeners())
|
||||
{
|
||||
l.synchronizeMailboxAddOrUpdateMessage(account, folder, localMessage);
|
||||
if (!localMessage.isSet(Flag.SEEN))
|
||||
{
|
||||
l.synchronizeMailboxNewMessage(account, folder, localMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1251,6 +1247,13 @@ public class MessagingController implements Runnable
|
||||
* fetch results for newest to oldest. If not, no harm done.
|
||||
*/
|
||||
Collections.reverse(unsyncedMessages);
|
||||
int visibleLimit = localFolder.getVisibleLimit();
|
||||
int listSize = unsyncedMessages.size();
|
||||
|
||||
if (listSize > visibleLimit)
|
||||
{
|
||||
unsyncedMessages = unsyncedMessages.subList(listSize - visibleLimit, listSize);
|
||||
}
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
if (remoteFolder.supportsFetchingFlags())
|
||||
@ -1281,9 +1284,9 @@ public class MessagingController implements Runnable
|
||||
|
||||
// Store the new message locally
|
||||
localFolder.appendMessages(new Message[]
|
||||
{
|
||||
message
|
||||
});
|
||||
{
|
||||
message
|
||||
});
|
||||
|
||||
if (message.getSize() > (MAX_SMALL_MESSAGE_SIZE))
|
||||
{
|
||||
@ -1556,6 +1559,19 @@ public class MessagingController implements Runnable
|
||||
}
|
||||
Log.i(Email.LOG_TAG, "SYNC: Synced remote messages for folder " + folder + ", " + newMessages.get() + " new messages");
|
||||
|
||||
localFolder.purgeToVisibleLimit(new MessageRemovalListener()
|
||||
{
|
||||
@Override
|
||||
public void messageRemoved(Message message)
|
||||
{
|
||||
for (MessagingListener l : getListeners())
|
||||
{
|
||||
l.synchronizeMailboxRemovedMessage(account, folder, message);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return newMessages.get();
|
||||
}
|
||||
|
||||
@ -2072,7 +2088,7 @@ public class MessagingController implements Runnable
|
||||
}
|
||||
|
||||
String rootCauseMessage = getRootCauseMessage(t);
|
||||
log("Error" + "'" + rootCauseMessage + "'");
|
||||
Log.e(Email.LOG_TAG, "Error " + "'" + rootCauseMessage + "'", t);
|
||||
|
||||
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
|
||||
LocalFolder localFolder = (LocalFolder)localStore.getFolder(account.getErrorFolderName());
|
||||
@ -2474,9 +2490,9 @@ public class MessagingController implements Runnable
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(new Message[]
|
||||
{
|
||||
message
|
||||
}, fp, null);
|
||||
{
|
||||
message
|
||||
}, fp, null);
|
||||
localFolder.close(false);
|
||||
if (!message.isSet(Flag.SEEN))
|
||||
{
|
||||
@ -2753,9 +2769,9 @@ public class MessagingController implements Runnable
|
||||
(LocalFolder) localStore.getFolder(account.getOutboxFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
localFolder.appendMessages(new Message[]
|
||||
{
|
||||
message
|
||||
});
|
||||
{
|
||||
message
|
||||
});
|
||||
Message localMessage = localFolder.getMessage(message.getUid());
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
localFolder.close(false);
|
||||
@ -3799,9 +3815,9 @@ public class MessagingController implements Runnable
|
||||
(LocalFolder) localStore.getFolder(account.getDraftsFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
localFolder.appendMessages(new Message[]
|
||||
{
|
||||
message
|
||||
});
|
||||
{
|
||||
message
|
||||
});
|
||||
Message localMessage = localFolder.getMessage(message.getUid());
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
|
||||
@ -3989,15 +4005,15 @@ public class MessagingController implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
public void messagesFlagsChanged(String folderName,
|
||||
public void messagesFlagsChanged(Folder folder,
|
||||
List<Message> messages)
|
||||
{
|
||||
controller.messagesArrived(account, folderName, messages, false);
|
||||
controller.messagesArrived(account, folder, messages, true);
|
||||
|
||||
}
|
||||
public void messagesArrived(String folderName, List<Message> messages)
|
||||
public void messagesArrived(Folder folder, List<Message> messages)
|
||||
{
|
||||
controller.messagesArrived(account, folderName, messages, true);
|
||||
controller.messagesArrived(account, folder, messages, false);
|
||||
}
|
||||
|
||||
public void sleep(long millis)
|
||||
@ -4152,44 +4168,37 @@ public class MessagingController implements Runnable
|
||||
|
||||
}
|
||||
|
||||
public void messagesArrived(final Account account, final String folderName, final List<Message> messages, final boolean doNotify)
|
||||
public void messagesArrived(final Account account, final Folder remoteFolder, final List<Message> messages, final boolean flagSyncOnly)
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Got new pushed email messages for account " + account.getDescription()
|
||||
+ ", folder " + folderName);
|
||||
+ ", folder " + remoteFolder.getName());
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
putBackground("Push messageArrived of account " + account.getDescription()
|
||||
+ ", folder " + folderName, null, new Runnable()
|
||||
+ ", folder " + remoteFolder.getName(), null, new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
LocalFolder localFolder = null;
|
||||
Folder remoteFolder = null;
|
||||
try
|
||||
{
|
||||
LocalStore localStore = (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
|
||||
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
|
||||
remoteFolder = remoteStore.getFolder(folderName);
|
||||
localFolder= (LocalFolder) localStore.getFolder(folderName);
|
||||
localFolder= (LocalFolder) localStore.getFolder(remoteFolder.getName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
remoteFolder.open(OpenMode.READ_WRITE);
|
||||
|
||||
int newCount = downloadMessages(account, remoteFolder, localFolder, messages);
|
||||
int newCount = downloadMessages(account, remoteFolder, localFolder, messages, flagSyncOnly);
|
||||
setLocalUnreadCountToRemote(localFolder, remoteFolder, messages.size());
|
||||
|
||||
localFolder.setLastPush(System.currentTimeMillis());
|
||||
localFolder.setStatus(null);
|
||||
|
||||
int unreadMessageCount = account.getUnreadMessageCount(mApplication, mApplication);
|
||||
if (doNotify && newCount > 0 && unreadMessageCount > 0)
|
||||
{
|
||||
notifyAccount(mApplication, account, newCount, unreadMessageCount);
|
||||
}
|
||||
if (unreadMessageCount == 0)
|
||||
{
|
||||
notifyAccount(mApplication, account, newCount, unreadMessageCount);
|
||||
}
|
||||
Log.i(Email.LOG_TAG, "messagesArrived newCount = " + newCount + ", unreadMessageCount = " + unreadMessageCount);
|
||||
notifyAccount(mApplication, account, newCount, unreadMessageCount);
|
||||
|
||||
for (MessagingListener l : getListeners())
|
||||
{
|
||||
l.folderStatusChanged(account, folderName);
|
||||
l.folderStatusChanged(account, remoteFolder.getName());
|
||||
l.accountStatusChanged(account, unreadMessageCount);
|
||||
}
|
||||
|
||||
@ -4208,7 +4217,7 @@ public class MessagingController implements Runnable
|
||||
}
|
||||
for (MessagingListener l : getListeners())
|
||||
{
|
||||
l.synchronizeMailboxFailed(account, folderName, errorMessage);
|
||||
l.synchronizeMailboxFailed(account, remoteFolder.getName(), errorMessage);
|
||||
}
|
||||
addErrorMessage(account, e);
|
||||
}
|
||||
@ -4225,17 +4234,6 @@ public class MessagingController implements Runnable
|
||||
Log.e(Email.LOG_TAG, "Unable to close localFolder", e);
|
||||
}
|
||||
}
|
||||
if (remoteFolder != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
remoteFolder.close(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Unable to close remoteFolder", e);
|
||||
}
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@ -4249,7 +4247,7 @@ public class MessagingController implements Runnable
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Interrupted while awaiting latch release", e);
|
||||
}
|
||||
Log.i(Email.LOG_TAG, "Latch released");
|
||||
Log.i(Email.LOG_TAG, "MessagingController.messagesArrivedLatch released");
|
||||
}
|
||||
enum MemorizingState { STARTED, FINISHED, FAILED };
|
||||
|
||||
|
6
src/com/android/email/mail/MessageRemovalListener.java
Normal file
6
src/com/android/email/mail/MessageRemovalListener.java
Normal file
@ -0,0 +1,6 @@
|
||||
package com.android.email.mail;
|
||||
|
||||
public interface MessageRemovalListener
|
||||
{
|
||||
public void messageRemoved(Message message);
|
||||
}
|
@ -6,8 +6,8 @@ public interface PushReceiver
|
||||
{
|
||||
public void acquireWakeLock();
|
||||
public void releaseWakeLock();
|
||||
public void messagesArrived(String folderName, List<Message> mess);
|
||||
public void messagesFlagsChanged(String folderName, List<Message> mess);
|
||||
public void messagesArrived(Folder folder, List<Message> mess);
|
||||
public void messagesFlagsChanged(Folder folder, List<Message> mess);
|
||||
public String getPushState(String folderName);
|
||||
public void pushError(String errorMessage, Exception e);
|
||||
public void setPushActive(String folderName, boolean enabled);
|
||||
|
@ -464,6 +464,12 @@ public class ImapStore extends Store
|
||||
public void open(OpenMode mode) throws MessagingException
|
||||
{
|
||||
internalOpen(mode);
|
||||
|
||||
if (mMessageCount == -1)
|
||||
{
|
||||
throw new MessagingException(
|
||||
"Did not find message count during open");
|
||||
}
|
||||
}
|
||||
|
||||
public List<ImapResponse> internalOpen(OpenMode mode) throws MessagingException
|
||||
@ -559,13 +565,8 @@ public class ImapStore extends Store
|
||||
}
|
||||
}
|
||||
|
||||
if (mMessageCount == -1)
|
||||
{
|
||||
throw new MessagingException(
|
||||
"Did not find message count with command '" + command + "'");
|
||||
}
|
||||
mExists = true;
|
||||
return null;
|
||||
return responses;
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
@ -594,7 +595,7 @@ public class ImapStore extends Store
|
||||
{
|
||||
if (mMessageCount != -1)
|
||||
{
|
||||
// close();
|
||||
// close();
|
||||
mMessageCount = -1;
|
||||
}
|
||||
if (!isOpen())
|
||||
@ -788,12 +789,12 @@ public class ImapStore extends Store
|
||||
public Message[] getMessages(int start, int end, MessageRetrievalListener listener)
|
||||
throws MessagingException
|
||||
{
|
||||
|
||||
|
||||
return getMessages(start, end, false, listener);
|
||||
}
|
||||
|
||||
protected Message[] getMessages(int start, int end, boolean includeDeleted, MessageRetrievalListener listener)
|
||||
|
||||
|
||||
protected Message[] getMessages(final int start, final int end, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
throws MessagingException
|
||||
{
|
||||
if (start < 1 || end < 1 || end < start)
|
||||
@ -802,22 +803,48 @@ public class ImapStore extends Store
|
||||
String.format("Invalid message set %d %d",
|
||||
start, end));
|
||||
}
|
||||
ImapSearcher searcher = new ImapSearcher()
|
||||
{
|
||||
public List<ImapResponse> search() throws IOException, MessagingException
|
||||
{
|
||||
return executeSimpleCommand(String.format("UID SEARCH %d:%d" + (includeDeleted ? "" : " NOT DELETED"), start, end));
|
||||
}
|
||||
};
|
||||
return search(searcher, listener);
|
||||
|
||||
}
|
||||
protected Message[] getMessages(final List<Integer> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
throws MessagingException
|
||||
{
|
||||
ImapSearcher searcher = new ImapSearcher()
|
||||
{
|
||||
public List<ImapResponse> search() throws IOException, MessagingException
|
||||
{
|
||||
return executeSimpleCommand(String.format("UID SEARCH %s" + (includeDeleted ? "" : " NOT DELETED"), Utility.combine(mesgSeqs.toArray(), ',')));
|
||||
}
|
||||
};
|
||||
return search(searcher, listener);
|
||||
}
|
||||
|
||||
private Message[] search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException
|
||||
{
|
||||
|
||||
checkOpen();
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
try
|
||||
{
|
||||
boolean gotSearchValues = false;
|
||||
ArrayList<Integer> uids = new ArrayList<Integer>();
|
||||
List<ImapResponse> responses = executeSimpleCommand(String.format("UID SEARCH %d:%d" + (includeDeleted ? "" : " NOT DELETED"), start, end));
|
||||
List<ImapResponse> responses = searcher.search(); //
|
||||
for (ImapResponse response : responses)
|
||||
{
|
||||
// Log.d(Email.LOG_TAG, "Got search response: " + response.get(0) + ", size " + response.size());
|
||||
// Log.d(Email.LOG_TAG, "Got search response: " + response.get(0) + ", size " + response.size());
|
||||
if (response.get(0).equals("SEARCH"))
|
||||
{
|
||||
gotSearchValues = true;
|
||||
for (int i = 1, count = response.size(); i < count; i++)
|
||||
{
|
||||
// Log.d(Email.LOG_TAG, "Got search response UID: " + response.getString(i));
|
||||
// Log.d(Email.LOG_TAG, "Got search response UID: " + response.getString(i));
|
||||
|
||||
uids.add(Integer.parseInt(response.getString(i)));
|
||||
}
|
||||
@ -975,7 +1002,6 @@ public class ImapStore extends Store
|
||||
do
|
||||
{
|
||||
response = mConnection.readResponse();
|
||||
handleUntaggedResponse(response);
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "response for fetch: " + response + " for " + getLogId());
|
||||
@ -988,7 +1014,8 @@ public class ImapStore extends Store
|
||||
Message message = messageMap.get(uid);
|
||||
if (message == null)
|
||||
{
|
||||
Log.w(Email.LOG_TAG, "Do not have message in messageMap for UID " + uid + " for " + getLogId());
|
||||
Log.d(Email.LOG_TAG, "Do not have message in messageMap for UID " + uid + " for " + getLogId());
|
||||
handleUntaggedResponse(response);
|
||||
continue;
|
||||
}
|
||||
if (listener != null)
|
||||
@ -1107,6 +1134,10 @@ public class ImapStore extends Store
|
||||
listener.messageFinished(message, messageNumber, messageMap.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handleUntaggedResponse(response);
|
||||
}
|
||||
|
||||
while (response.more());
|
||||
|
||||
@ -1467,9 +1498,9 @@ public class ImapStore extends Store
|
||||
try
|
||||
{
|
||||
/*
|
||||
* Try to find the UID of the message we just appended using the
|
||||
* Message-ID header.
|
||||
*/
|
||||
* Try to find the UID of the message we just appended using the
|
||||
* Message-ID header.
|
||||
*/
|
||||
String[] messageIdHeader = message.getHeader("Message-ID");
|
||||
|
||||
if (messageIdHeader == null || messageIdHeader.length == 0)
|
||||
@ -1743,9 +1774,9 @@ public class ImapStore extends Store
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
final boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
sslContext.init(null, new TrustManager[]
|
||||
{
|
||||
TrustManagerFactory.get(mHost, secure)
|
||||
}, new SecureRandom());
|
||||
{
|
||||
TrustManagerFactory.get(mHost, secure)
|
||||
}, new SecureRandom());
|
||||
mSocket = sslContext.getSocketFactory().createSocket();
|
||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||
}
|
||||
@ -1783,10 +1814,10 @@ public class ImapStore extends Store
|
||||
{
|
||||
if (capability instanceof String)
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
|
||||
}
|
||||
// if (Email.DEBUG)
|
||||
// {
|
||||
// Log.v(Email.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
|
||||
// }
|
||||
capabilities.add((String)capability);
|
||||
}
|
||||
}
|
||||
@ -1807,9 +1838,9 @@ public class ImapStore extends Store
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
sslContext.init(null, new TrustManager[]
|
||||
{
|
||||
TrustManagerFactory.get(mHost, secure)
|
||||
}, new SecureRandom());
|
||||
{
|
||||
TrustManagerFactory.get(mHost, secure)
|
||||
}, new SecureRandom());
|
||||
mSocket = sslContext.getSocketFactory().createSocket(mSocket, mHost, mPort,
|
||||
true);
|
||||
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
||||
@ -1886,10 +1917,10 @@ public class ImapStore extends Store
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Connection " + getLogId() + " has " + capabilities.size() + " capabilities");
|
||||
for (String capability : capabilities)
|
||||
{
|
||||
Log.v(Email.LOG_TAG, "Have capability '" + capability + "' for " + getLogId());
|
||||
}
|
||||
// for (String capability : capabilities)
|
||||
// {
|
||||
// Log.v(Email.LOG_TAG, "Have capability '" + capability + "' for " + getLogId());
|
||||
// }
|
||||
}
|
||||
return capabilities.contains("IDLE");
|
||||
}
|
||||
@ -2223,6 +2254,7 @@ public class ImapStore extends Store
|
||||
final AtomicBoolean idling = new AtomicBoolean(false);
|
||||
final AtomicBoolean doneSent = new AtomicBoolean(false);
|
||||
final AtomicInteger delayTime = new AtomicInteger(NORMAL_DELAY_TIME);
|
||||
List<ImapResponse> storedUntaggedResponses = new ArrayList<ImapResponse>();
|
||||
|
||||
public ImapFolderPusher(ImapStore store, String name, PushReceiver nReceiver)
|
||||
{
|
||||
@ -2265,6 +2297,7 @@ public class ImapStore extends Store
|
||||
Log.i(Email.LOG_TAG, "Pusher starting for " + getLogId());
|
||||
while (stop.get() != true)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
int oldUidNext = -1;
|
||||
@ -2328,14 +2361,25 @@ public class ImapStore extends Store
|
||||
{
|
||||
if (stop.get() != true)
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "About to IDLE for " + getLogId());
|
||||
List<ImapResponse> untaggedResponses = null;
|
||||
if (storedUntaggedResponses.size() > 0)
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Processing " + storedUntaggedResponses.size() + " from previous commands for " + getLogId());
|
||||
untaggedResponses = new ArrayList<ImapResponse>(storedUntaggedResponses);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "About to IDLE for " + getLogId());
|
||||
|
||||
receiver.setPushActive(getName(), true);
|
||||
idling.set(true);
|
||||
doneSent.set(false);
|
||||
executeSimpleCommand("IDLE", false, ImapFolderPusher.this);
|
||||
idling.set(false);
|
||||
receiver.setPushActive(getName(), false);
|
||||
receiver.setPushActive(getName(), true);
|
||||
idling.set(true);
|
||||
doneSent.set(false);
|
||||
untaggedResponses = executeSimpleCommand("IDLE", false, ImapFolderPusher.this);
|
||||
idling.set(false);
|
||||
|
||||
}
|
||||
storedUntaggedResponses.clear();
|
||||
processUntaggedResponses(untaggedResponses);
|
||||
delayTime.set(NORMAL_DELAY_TIME);
|
||||
}
|
||||
}
|
||||
@ -2343,6 +2387,7 @@ public class ImapStore extends Store
|
||||
catch (Exception e)
|
||||
{
|
||||
receiver.acquireWakeLock();
|
||||
storedUntaggedResponses.clear();
|
||||
idling.set(false);
|
||||
receiver.setPushActive(getName(), false);
|
||||
try
|
||||
@ -2372,6 +2417,7 @@ public class ImapStore extends Store
|
||||
}
|
||||
}
|
||||
}
|
||||
receiver.setPushActive(getName(), false);
|
||||
try
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Pusher for " + getLogId() + " is exiting");
|
||||
@ -2391,39 +2437,50 @@ public class ImapStore extends Store
|
||||
listeningThread.start();
|
||||
}
|
||||
|
||||
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
|
||||
|
||||
protected List<ImapResponse> handleUntaggedResponses(List<ImapResponse> responses)
|
||||
@Override
|
||||
protected void handleUntaggedResponse(ImapResponse response)
|
||||
{
|
||||
flagSyncMsgSeqs.clear();
|
||||
int oldMessageCount = mMessageCount;
|
||||
|
||||
super.handleUntaggedResponses(responses);
|
||||
|
||||
List<Integer> flagSyncMsgSeqsCopy = new ArrayList<Integer>();
|
||||
flagSyncMsgSeqsCopy.addAll(flagSyncMsgSeqs);
|
||||
|
||||
|
||||
if (Email.DEBUG)
|
||||
if (response.mTag == null && response.size() > 1)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "oldMessageCount = " + oldMessageCount + ", new mMessageCount = " + mMessageCount
|
||||
+ " for " + getLogId());
|
||||
Object responseType = response.get(1);
|
||||
if ("FETCH".equals(responseType)
|
||||
|| "EXPUNGE".equals(responseType)
|
||||
|| "EXISTS".equals(responseType))
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Storing response " + response + " for later processing");
|
||||
}
|
||||
storedUntaggedResponses.add(response);
|
||||
}
|
||||
}
|
||||
if (oldMessageCount > 0 && mMessageCount > oldMessageCount)
|
||||
}
|
||||
|
||||
protected void processUntaggedResponses(List<ImapResponse> responses)
|
||||
{
|
||||
int oldMessageCount = mMessageCount;
|
||||
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
|
||||
|
||||
for (ImapResponse response : responses)
|
||||
{
|
||||
oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs);
|
||||
}
|
||||
if (oldMessageCount < 0)
|
||||
{
|
||||
oldMessageCount = 0;
|
||||
}
|
||||
if (mMessageCount > oldMessageCount)
|
||||
{
|
||||
syncMessages(oldMessageCount + 1, mMessageCount, true);
|
||||
}
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "There are " + flagSyncMsgSeqsCopy + " messages needing flag sync for " + getLogId());
|
||||
Log.d(Email.LOG_TAG, "There are " + flagSyncMsgSeqs + " messages needing flag sync for " + getLogId());
|
||||
}
|
||||
// TODO: Identify ranges and call syncMessages on said identified ranges
|
||||
for (Integer msgSeq : flagSyncMsgSeqsCopy)
|
||||
if (flagSyncMsgSeqs.size() > 0)
|
||||
{
|
||||
syncMessages(msgSeq, msgSeq, false);
|
||||
syncMessages(flagSyncMsgSeqs);
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
private void syncMessages(int start, int end, boolean newArrivals)
|
||||
@ -2448,9 +2505,32 @@ public class ImapStore extends Store
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleUntaggedResponse(ImapResponse response)
|
||||
private void syncMessages(List<Integer> flagSyncMsgSeqs)
|
||||
{
|
||||
try
|
||||
{
|
||||
Message[] messageArray = null;
|
||||
|
||||
messageArray = getMessages(flagSyncMsgSeqs, true, null);
|
||||
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
for (Message message : messageArray)
|
||||
{
|
||||
messages.add(message);
|
||||
}
|
||||
pushMessages(messages, false);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
receiver.pushError("Exception while processing Push untagged responses", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected int processUntaggedResponse(int oldMessageCount, ImapResponse response, List<Integer> flagSyncMsgSeqs)
|
||||
{
|
||||
super.handleUntaggedResponse(response);
|
||||
int messageCountDelta = 0;
|
||||
if (response.mTag == null && response.size() > 1)
|
||||
{
|
||||
try
|
||||
@ -2463,7 +2543,37 @@ public class ImapStore extends Store
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got untagged FETCH for msgseq " + msgSeq + " for " + getLogId());
|
||||
}
|
||||
flagSyncMsgSeqs.add(msgSeq);
|
||||
if (flagSyncMsgSeqs.contains(msgSeq) == false)
|
||||
{
|
||||
flagSyncMsgSeqs.add(msgSeq);
|
||||
}
|
||||
}
|
||||
if ("EXPUNGE".equals(responseType))
|
||||
{
|
||||
int msgSeq = response.getNumber(0);
|
||||
if (msgSeq <= oldMessageCount)
|
||||
{
|
||||
messageCountDelta = -1;
|
||||
}
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got untagged EXPUNGE for msgseq " + msgSeq + " for " + getLogId());
|
||||
}
|
||||
List<Integer> newSeqs = new ArrayList<Integer>();
|
||||
Iterator<Integer> flagIter = flagSyncMsgSeqs.iterator();
|
||||
while (flagIter.hasNext())
|
||||
{
|
||||
Integer flagMsg = flagIter.next();
|
||||
if (flagMsg >= msgSeq)
|
||||
{
|
||||
flagIter.remove();
|
||||
if (flagMsg > msgSeq)
|
||||
{
|
||||
newSeqs.add(flagMsg--);
|
||||
}
|
||||
}
|
||||
}
|
||||
flagSyncMsgSeqs.addAll(newSeqs);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -2471,6 +2581,7 @@ public class ImapStore extends Store
|
||||
Log.e(Email.LOG_TAG, "Could not handle untagged FETCH for " + getLogId(), e);
|
||||
}
|
||||
}
|
||||
return messageCountDelta;
|
||||
}
|
||||
|
||||
|
||||
@ -2481,11 +2592,11 @@ public class ImapStore extends Store
|
||||
{
|
||||
if (newArrivals)
|
||||
{
|
||||
receiver.messagesArrived(getName(), messages);
|
||||
receiver.messagesArrived(this, messages);
|
||||
}
|
||||
else
|
||||
{
|
||||
receiver.messagesFlagsChanged(getName(), messages);
|
||||
receiver.messagesFlagsChanged(this, messages);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
@ -2684,4 +2795,8 @@ public class ImapStore extends Store
|
||||
}
|
||||
|
||||
}
|
||||
private interface ImapSearcher
|
||||
{
|
||||
List<ImapResponse> search() throws IOException, MessagingException;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import com.android.email.mail.FetchProfile;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessageRemovalListener;
|
||||
import com.android.email.mail.MessageRetrievalListener;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Part;
|
||||
@ -610,9 +611,9 @@ public class LocalStore extends Store implements Serializable
|
||||
cursor = mDb.rawQuery("SELECT id, unread_count, visible_limit, last_updated, status, push_state, last_pushed FROM folders "
|
||||
+ "where folders.name = ?",
|
||||
new String[]
|
||||
{
|
||||
mName
|
||||
});
|
||||
{
|
||||
mName
|
||||
});
|
||||
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
@ -705,10 +706,10 @@ public class LocalStore extends Store implements Serializable
|
||||
throw new MessagingException("Folder " + mName + " already exists.");
|
||||
}
|
||||
mDb.execSQL("INSERT INTO folders (name, visible_limit) VALUES (?, ?)", new Object[]
|
||||
{
|
||||
mName,
|
||||
Email.DEFAULT_VISIBLE_LIMIT
|
||||
});
|
||||
{
|
||||
mName,
|
||||
Email.DEFAULT_VISIBLE_LIMIT
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -719,10 +720,10 @@ public class LocalStore extends Store implements Serializable
|
||||
throw new MessagingException("Folder " + mName + " already exists.");
|
||||
}
|
||||
mDb.execSQL("INSERT INTO folders (name, visible_limit) VALUES (?, ?)", new Object[]
|
||||
{
|
||||
mName,
|
||||
visibleLimit
|
||||
});
|
||||
{
|
||||
mName,
|
||||
visibleLimit
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -745,9 +746,9 @@ public class LocalStore extends Store implements Serializable
|
||||
{
|
||||
cursor = mDb.rawQuery("SELECT COUNT(*) FROM messages WHERE messages.folder_id = ?",
|
||||
new String[]
|
||||
{
|
||||
Long.toString(mFolderId)
|
||||
});
|
||||
{
|
||||
Long.toString(mFolderId)
|
||||
});
|
||||
cursor.moveToFirst();
|
||||
int messageCount = cursor.getInt(0);
|
||||
return messageCount;
|
||||
@ -799,6 +800,23 @@ public class LocalStore extends Store implements Serializable
|
||||
return mVisibleLimit;
|
||||
}
|
||||
|
||||
public void purgeToVisibleLimit(MessageRemovalListener listener) throws MessagingException
|
||||
{
|
||||
open(OpenMode.READ_WRITE);
|
||||
Message[] messages = getMessages(null);
|
||||
for (int i = 0; i < messages.length; i++)
|
||||
{
|
||||
if (i >= mVisibleLimit)
|
||||
{
|
||||
if (listener != null)
|
||||
{
|
||||
listener.messageRemoved(messages[i]);
|
||||
}
|
||||
messages[i].setFlag(Flag.X_DESTROYED, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setVisibleLimit(int visibleLimit) throws MessagingException
|
||||
{
|
||||
@ -1060,19 +1078,19 @@ public class LocalStore extends Store implements Serializable
|
||||
cursor = mDb.query(
|
||||
"attachments",
|
||||
new String[]
|
||||
{
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mime_type",
|
||||
"store_data",
|
||||
"content_uri"
|
||||
},
|
||||
"message_id = ?",
|
||||
new String[] { Long.toString(localMessage.mId) },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
{
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mime_type",
|
||||
"store_data",
|
||||
"content_uri"
|
||||
},
|
||||
"message_id = ?",
|
||||
new String[] { Long.toString(localMessage.mId) },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
while (cursor.moveToNext())
|
||||
{
|
||||
@ -1227,9 +1245,9 @@ public class LocalStore extends Store implements Serializable
|
||||
+ "bcc_list, reply_to_list, attachment_count, internal_date, message_id "
|
||||
+ "FROM messages " + "WHERE uid = ? " + "AND folder_id = ?",
|
||||
new String[]
|
||||
{
|
||||
message.getUid(), Long.toString(mFolderId)
|
||||
});
|
||||
{
|
||||
message.getUid(), Long.toString(mFolderId)
|
||||
});
|
||||
if (!cursor.moveToNext())
|
||||
{
|
||||
return null;
|
||||
@ -1272,9 +1290,9 @@ public class LocalStore extends Store implements Serializable
|
||||
+ (includeDeleted ? "" : "deleted = 0")
|
||||
+ " folder_id = ? ORDER BY date DESC"
|
||||
, new String[]
|
||||
{
|
||||
Long.toString(mFolderId)
|
||||
});
|
||||
{
|
||||
Long.toString(mFolderId)
|
||||
});
|
||||
|
||||
|
||||
int i = 0;
|
||||
@ -1369,11 +1387,11 @@ public class LocalStore extends Store implements Serializable
|
||||
message.setUid(Email.LOCAL_UID_PREFIX + UUID.randomUUID().toString());
|
||||
|
||||
mDb.execSQL("UPDATE messages " + "SET folder_id = ?, uid = ? " + "WHERE id = ?", new Object[]
|
||||
{
|
||||
lDestFolder.getId(),
|
||||
message.getUid(),
|
||||
lMessage.getId()
|
||||
});
|
||||
{
|
||||
lDestFolder.getId(),
|
||||
message.getUid(),
|
||||
lMessage.getId()
|
||||
});
|
||||
|
||||
LocalMessage placeHolder = new LocalMessage(oldUID, this);
|
||||
placeHolder.setFlagInternal(Flag.DELETED, true);
|
||||
@ -1561,27 +1579,27 @@ public class LocalStore extends Store implements Serializable
|
||||
+ "html_content = ?, text_content = ?, reply_to_list = ?, "
|
||||
+ "attachment_count = ? WHERE id = ?",
|
||||
new Object[]
|
||||
{
|
||||
message.getUid(),
|
||||
message.getSubject(),
|
||||
Address.pack(message.getFrom()),
|
||||
message.getSentDate() == null ? System
|
||||
.currentTimeMillis() : message.getSentDate()
|
||||
.getTime(),
|
||||
Utility.combine(message.getFlags(), ',').toUpperCase(),
|
||||
mFolderId,
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.TO)),
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.CC)),
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.BCC)),
|
||||
html.length() > 0 ? html : null,
|
||||
text.length() > 0 ? text : null,
|
||||
Address.pack(message.getReplyTo()),
|
||||
attachments.size(),
|
||||
message.mId
|
||||
});
|
||||
{
|
||||
message.getUid(),
|
||||
message.getSubject(),
|
||||
Address.pack(message.getFrom()),
|
||||
message.getSentDate() == null ? System
|
||||
.currentTimeMillis() : message.getSentDate()
|
||||
.getTime(),
|
||||
Utility.combine(message.getFlags(), ',').toUpperCase(),
|
||||
mFolderId,
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.TO)),
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.CC)),
|
||||
Address.pack(message
|
||||
.getRecipients(RecipientType.BCC)),
|
||||
html.length() > 0 ? html : null,
|
||||
text.length() > 0 ? text : null,
|
||||
Address.pack(message.getReplyTo()),
|
||||
attachments.size(),
|
||||
message.mId
|
||||
});
|
||||
|
||||
for (int i = 0, count = attachments.size(); i < count; i++)
|
||||
{
|
||||
@ -1621,9 +1639,9 @@ public class LocalStore extends Store implements Serializable
|
||||
{
|
||||
mDb.execSQL("DELETE FROM headers WHERE id = ?",
|
||||
new Object[]
|
||||
{
|
||||
id
|
||||
});
|
||||
{
|
||||
id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1802,9 +1820,9 @@ public class LocalStore extends Store implements Serializable
|
||||
{
|
||||
open(OpenMode.READ_ONLY);
|
||||
mDb.execSQL("DELETE FROM messages WHERE folder_id = ? and date < ?", new Object[]
|
||||
{
|
||||
Long.toString(mFolderId), new Long(cutoff)
|
||||
});
|
||||
{
|
||||
Long.toString(mFolderId), new Long(cutoff)
|
||||
});
|
||||
resetUnreadCount();
|
||||
}
|
||||
|
||||
@ -1840,9 +1858,9 @@ public class LocalStore extends Store implements Serializable
|
||||
deleteAttachments(message.getUid());
|
||||
}
|
||||
mDb.execSQL("DELETE FROM folders WHERE id = ?", new Object[]
|
||||
{
|
||||
Long.toString(mFolderId),
|
||||
});
|
||||
{
|
||||
Long.toString(mFolderId),
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2158,18 +2176,18 @@ public class LocalStore extends Store implements Serializable
|
||||
"reply_to_list = NULL " +
|
||||
"WHERE id = ?",
|
||||
new Object[]
|
||||
{
|
||||
mId
|
||||
});
|
||||
{
|
||||
mId
|
||||
});
|
||||
|
||||
/*
|
||||
* Delete all of the messages' attachments to save space.
|
||||
*/
|
||||
mDb.execSQL("DELETE FROM attachments WHERE id = ?",
|
||||
new Object[]
|
||||
{
|
||||
mId
|
||||
});
|
||||
{
|
||||
mId
|
||||
});
|
||||
|
||||
((LocalFolder)mFolder).deleteHeaders(mId);
|
||||
|
||||
@ -2213,9 +2231,9 @@ public class LocalStore extends Store implements Serializable
|
||||
* Set the flags on the message.
|
||||
*/
|
||||
mDb.execSQL("UPDATE messages " + "SET flags = ? " + " WHERE id = ?", new Object[]
|
||||
{
|
||||
Utility.combine(getFlags(), ',').toUpperCase(), mId
|
||||
});
|
||||
{
|
||||
Utility.combine(getFlags(), ',').toUpperCase(), mId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user