1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-12 04:25:08 -05:00

Fixes Issue 827

Fixes Issue 734

On reconnection, IMAP IDLE folders will now fetch up to the most
recent 10 message that arrived while a IDLE connection was not
available.

Fixes Issue 232

A serving of NAMESPACE-based auto-configuration on the side.  If the
IMAP prefix is empty, and the IMAP server supports NAMESPACE, use the
IMAP prefix supplied by NAMESPACE.  Also, if the user manually puts
the separator as the last character in the prefix, don't append the
separator.

Also:
Improved reliability of IMAP IDLE incoming message and flag state
change handling.  Reduction (but not elimination) of multiple
connections under startup conditions.
This commit is contained in:
Daniel Applebaum 2009-11-28 14:51:44 +00:00
parent b18e88cf0d
commit d871d2d2ee
9 changed files with 482 additions and 314 deletions

View File

@ -289,7 +289,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="account_setup_incoming_delete_policy_markread_label">Mark as read on server</string> <string name="account_setup_incoming_delete_policy_markread_label">Mark as read on server</string>
<string name="account_setup_incoming_imap_path_prefix_label">IMAP path prefix</string> <string name="account_setup_incoming_imap_path_prefix_label">IMAP path prefix</string>
<string name="account_setup_incoming_imap_path_prefix_hint">Optional</string> <string name="account_setup_incoming_imap_path_prefix_hint">(Automatic using NAMESPACE if available)</string>
<string name="account_setup_incoming_imap_folder_drafts">Drafts folder name</string> <string name="account_setup_incoming_imap_folder_drafts">Drafts folder name</string>
<string name="account_setup_incoming_imap_folder_sent">Sent folder name</string> <string name="account_setup_incoming_imap_folder_sent">Sent folder name</string>

View File

@ -832,6 +832,12 @@ public class Account implements Serializable
return super.equals(o); return super.equals(o);
} }
@Override
public int hashCode()
{
return mUuid.hashCode();
}
public FolderMode getFolderDisplayMode() public FolderMode getFolderDisplayMode()
{ {
return mFolderDisplayMode; return mFolderDisplayMode;

View File

@ -3995,147 +3995,15 @@ public class MessagingController implements Runnable
return pushers.values(); return pushers.values();
} }
public Pusher setupPushing(final Account account) public boolean setupPushing(final Account account)
{
Pusher pusher = pushers.get(account);
if (pusher != null)
{
return pusher;
}
Store store = null;
try
{
store = Store.getInstance(account.getStoreUri(), mApplication);
if (store.isPushCapable() == false)
{
Log.i(Email.LOG_TAG, "Account " + account.getDescription() + " is not push capable, skipping");
return null;
}
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Could not get remote store", e);
return null;
}
final MessagingController controller = this;
PushReceiver receiver = new PushReceiver()
{
ThreadLocal<WakeLock> threadWakeLock = new ThreadLocal<WakeLock>();
public void acquireWakeLock()
{
WakeLock wakeLock = threadWakeLock.get();
if (wakeLock == null)
{
PowerManager pm = (PowerManager) mApplication.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email");
wakeLock.setReferenceCounted(false);
threadWakeLock.set(wakeLock);
}
wakeLock.acquire(Email.PUSH_WAKE_LOCK_TIMEOUT);
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Acquired WakeLock for Pushing for thread " + Thread.currentThread().getName());
}
}
public void releaseWakeLock()
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Considering releasing WakeLock for Pushing");
}
WakeLock wakeLock = threadWakeLock.get();
if (wakeLock != null)
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Releasing WakeLock for Pushing for thread " + Thread.currentThread().getName());
}
wakeLock.release();
}
else
{
Log.e(Email.LOG_TAG, "No WakeLock waiting to be released for thread " + Thread.currentThread().getName());
}
}
public void messagesFlagsChanged(Folder folder,
List<Message> messages)
{
controller.messagesArrived(account, folder, messages, true);
}
public void messagesArrived(Folder folder, List<Message> messages)
{
controller.messagesArrived(account, folder, messages, false);
}
public void sleep(long millis)
{
SleepService.sleep(mApplication, millis, threadWakeLock.get(), Email.PUSH_WAKE_LOCK_TIMEOUT);
}
public void pushError(String errorMessage, Exception e)
{
String errMess = errorMessage;
String body = null;
if (errMess == null && e != null)
{
errMess = e.getMessage();
}
body = errMess;
if (e != null)
{
body = e.toString();
}
controller.addErrorMessage(account, errMess, body);
}
public String getPushState(String folderName)
{
LocalFolder localFolder = null;
try
{
LocalStore localStore = (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
localFolder= (LocalFolder) localStore.getFolder(folderName);
localFolder.open(OpenMode.READ_WRITE);
return localFolder.getPushState();
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Unable to get push state from account " + account.getDescription()
+ ", folder " + folderName, e);
return null;
}
finally
{
if (localFolder != null)
{ {
try try
{ {
localFolder.close(false); Pusher previousPusher = pushers.remove(account);
} if (previousPusher != null)
catch (Exception e)
{ {
Log.e(Email.LOG_TAG, "Unable to close folder '" + folderName + "' in account " + account.getDescription(), e); previousPusher.stop();
} }
}
}
}
public void setPushActive(String folderName, boolean enabled)
{
for (MessagingListener l : getListeners())
{
l.setPushActive(account, folderName, enabled);
}
}
};
try
{
Preferences prefs = Preferences.getPreferences(mApplication); Preferences prefs = Preferences.getPreferences(mApplication);
Account.FolderMode aDisplayMode = account.getFolderDisplayMode(); Account.FolderMode aDisplayMode = account.getFolderDisplayMode();
@ -4178,19 +4046,46 @@ public class MessagingController implements Runnable
Log.i(Email.LOG_TAG, "Starting pusher for " + account.getDescription() + ":" + folder.getName()); Log.i(Email.LOG_TAG, "Starting pusher for " + account.getDescription() + ":" + folder.getName());
names.add(folder.getName()); names.add(folder.getName());
} }
if (names.size() > 0) if (names.size() > 0)
{ {
pusher = store.getPusher(receiver, names); PushReceiver receiver = new MessagingControllerPushReceiver(mApplication, account, this);
try
{
Store store = Store.getInstance(account.getStoreUri(), mApplication);
if (store.isPushCapable() == false)
{
Log.i(Email.LOG_TAG, "Account " + account.getDescription() + " is not push capable, skipping");
return false;
}
Pusher pusher = store.getPusher(receiver);
Pusher oldPusher = null;
if (pusher != null) if (pusher != null)
{ {
pushers.put(account, pusher); oldPusher = pushers.putIfAbsent(account, pusher);
} }
return pusher; if (oldPusher != null)
{
pusher = oldPusher;
}
else
{
pusher.start(names);
}
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Could not get remote store", e);
return false;
}
return true;
} }
else else
{ {
Log.i(Email.LOG_TAG, "No folders are configured for pushing in account " + account.getDescription()); Log.i(Email.LOG_TAG, "No folders are configured for pushing in account " + account.getDescription());
return null; return false;
} }
} }
@ -4198,16 +4093,7 @@ public class MessagingController implements Runnable
{ {
Log.e(Email.LOG_TAG, "Got exception while setting up pushing", e); Log.e(Email.LOG_TAG, "Got exception while setting up pushing", e);
} }
return null; return false;
}
public void stopPushing(Account account)
{
Pusher pusher = pushers.remove(account);
if (pusher != null)
{
pusher.stop();
}
} }
public void stopAllPushing() public void stopAllPushing()
@ -4220,7 +4106,6 @@ public class MessagingController implements Runnable
iter.remove(); iter.remove();
pusher.stop(); pusher.stop();
} }
} }
public void messagesArrived(final Account account, final Folder remoteFolder, final List<Message> messages, final boolean flagSyncOnly) public void messagesArrived(final Account account, final Folder remoteFolder, final List<Message> messages, final boolean flagSyncOnly)
@ -4239,7 +4124,6 @@ public class MessagingController implements Runnable
LocalStore localStore = (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication); LocalStore localStore = (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
localFolder= (LocalFolder) localStore.getFolder(remoteFolder.getName()); localFolder= (LocalFolder) localStore.getFolder(remoteFolder.getName());
localFolder.open(OpenMode.READ_WRITE); localFolder.open(OpenMode.READ_WRITE);
remoteFolder.open(OpenMode.READ_WRITE);
int newCount = downloadMessages(account, remoteFolder, localFolder, messages, flagSyncOnly); int newCount = downloadMessages(account, remoteFolder, localFolder, messages, flagSyncOnly);
setLocalUnreadCountToRemote(localFolder, remoteFolder, messages.size()); setLocalUnreadCountToRemote(localFolder, remoteFolder, messages.size());

View File

@ -0,0 +1,145 @@
package com.android.email;
import java.util.List;
import android.app.Application;
import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import com.android.email.mail.Folder;
import com.android.email.mail.Message;
import com.android.email.mail.PushReceiver;
import com.android.email.mail.Store;
import com.android.email.mail.Folder.OpenMode;
import com.android.email.mail.store.LocalStore;
import com.android.email.mail.store.LocalStore.LocalFolder;
import com.android.email.service.SleepService;
public class MessagingControllerPushReceiver implements PushReceiver
{
final Account account;
final MessagingController controller;
final Application mApplication;
public MessagingControllerPushReceiver(Application nApplication, Account nAccount, MessagingController nController)
{
account = nAccount;
controller = nController;
mApplication = nApplication;
}
ThreadLocal<WakeLock> threadWakeLock = new ThreadLocal<WakeLock>();
public void acquireWakeLock()
{
WakeLock wakeLock = threadWakeLock.get();
if (wakeLock == null)
{
PowerManager pm = (PowerManager) mApplication.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email");
wakeLock.setReferenceCounted(false);
threadWakeLock.set(wakeLock);
}
wakeLock.acquire(Email.PUSH_WAKE_LOCK_TIMEOUT);
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Acquired WakeLock for Pushing for thread " + Thread.currentThread().getName());
}
}
public void releaseWakeLock()
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Considering releasing WakeLock for Pushing");
}
WakeLock wakeLock = threadWakeLock.get();
if (wakeLock != null)
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Releasing WakeLock for Pushing for thread " + Thread.currentThread().getName());
}
wakeLock.release();
}
else
{
Log.e(Email.LOG_TAG, "No WakeLock waiting to be released for thread " + Thread.currentThread().getName());
}
}
public void messagesFlagsChanged(Folder folder,
List<Message> messages)
{
controller.messagesArrived(account, folder, messages, true);
}
public void messagesArrived(Folder folder, List<Message> messages)
{
controller.messagesArrived(account, folder, messages, false);
}
public void sleep(long millis)
{
SleepService.sleep(mApplication, millis, threadWakeLock.get(), Email.PUSH_WAKE_LOCK_TIMEOUT);
}
public void pushError(String errorMessage, Exception e)
{
String errMess = errorMessage;
String body = null;
if (errMess == null && e != null)
{
errMess = e.getMessage();
}
body = errMess;
if (e != null)
{
body = e.toString();
}
controller.addErrorMessage(account, errMess, body);
}
public String getPushState(String folderName)
{
LocalFolder localFolder = null;
try
{
LocalStore localStore = (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
localFolder= (LocalFolder) localStore.getFolder(folderName);
localFolder.open(OpenMode.READ_WRITE);
return localFolder.getPushState();
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Unable to get push state from account " + account.getDescription()
+ ", folder " + folderName, e);
return null;
}
finally
{
if (localFolder != null)
{
try
{
localFolder.close(false);
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Unable to close folder '" + folderName + "' in account " + account.getDescription(), e);
}
}
}
}
public void setPushActive(String folderName, boolean enabled)
{
for (MessagingListener l : controller.getListeners())
{
l.setPushActive(account, folderName, enabled);
}
}
}

View File

@ -1,9 +1,11 @@
package com.android.email.mail; package com.android.email.mail;
import java.util.List;
public interface Pusher public interface Pusher
{ {
public void start(); public void start(List<String> folderNames);
public void refresh(); public void refresh();
public void stop(); public void stop();
/** /**

View File

@ -113,7 +113,7 @@ public abstract class Store
{ {
} }
public Pusher getPusher(PushReceiver receiver, List<String> names) public Pusher getPusher(PushReceiver receiver)
{ {
return null; return null;
} }

View File

@ -33,6 +33,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -100,13 +101,19 @@ public class ImapStore extends Store
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN }; private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN };
private static final String CAPABILITY_IDLE = "IDLE";
private static final String COMMAND_IDLE = "IDLE";
private static final String CAPABILITY_NAMESPACE = "NAMESPACE";
private static final String COMMAND_NAMESPACE = "NAMESPACE";
private String mHost; private String mHost;
private int mPort; private int mPort;
private String mUsername; private String mUsername;
private String mPassword; private String mPassword;
private int mConnectionSecurity; private int mConnectionSecurity;
private String mPathPrefix; private volatile String mPathPrefix;
private String mPathDelimeter; private volatile String mCombinedPrefix = null;
private volatile String mPathDelimeter;
private LinkedList<ImapConnection> mConnections = private LinkedList<ImapConnection> mConnections =
new LinkedList<ImapConnection>(); new LinkedList<ImapConnection>();
@ -196,6 +203,10 @@ public class ImapStore extends Store
if ((uri.getPath() != null) && (uri.getPath().length() > 0)) if ((uri.getPath() != null) && (uri.getPath().length() > 0))
{ {
mPathPrefix = uri.getPath().substring(1); mPathPrefix = uri.getPath().substring(1);
if (mPathPrefix != null && mPathPrefix.trim().length() == 0)
{
mPathPrefix = null;
}
} }
mModifiedUtf7Charset = new CharsetProvider().charsetForName("X-RFC-3501"); mModifiedUtf7Charset = new CharsetProvider().charsetForName("X-RFC-3501");
@ -217,6 +228,34 @@ public class ImapStore extends Store
return folder; return folder;
} }
private String getCombinedPrefix()
{
if (mCombinedPrefix == null)
{
if (mPathPrefix != null)
{
String tmpPrefix = mPathPrefix.trim();
String tmpDelim = (mPathDelimeter != null ? mPathDelimeter.trim() : "");
if (tmpPrefix.endsWith(tmpDelim))
{
mCombinedPrefix = tmpPrefix;
}
else if (tmpPrefix.length() > 0)
{
mCombinedPrefix = tmpPrefix + tmpDelim;
}
else
{
mCombinedPrefix = "";
}
}
else
{
mCombinedPrefix = "";
}
}
return mCombinedPrefix;
}
@Override @Override
public Folder[] getPersonalNamespaces() throws MessagingException public Folder[] getPersonalNamespaces() throws MessagingException
@ -225,13 +264,10 @@ public class ImapStore extends Store
try try
{ {
ArrayList<Folder> folders = new ArrayList<Folder>(); ArrayList<Folder> folders = new ArrayList<Folder>();
if (mPathPrefix == null)
{
mPathPrefix = "";
}
List<ImapResponse> responses = List<ImapResponse> responses =
connection.executeSimpleCommand(String.format("LIST \"\" \"%s*\"", connection.executeSimpleCommand(String.format("LIST \"\" \"%s*\"",
mPathPrefix)); getCombinedPrefix()));
for (ImapResponse response : responses) for (ImapResponse response : responses)
{ {
@ -243,6 +279,7 @@ public class ImapStore extends Store
if (mPathDelimeter == null) if (mPathDelimeter == null)
{ {
mPathDelimeter = response.getString(2); mPathDelimeter = response.getString(2);
mCombinedPrefix = null;
} }
if (folder.equalsIgnoreCase(Email.INBOX)) if (folder.equalsIgnoreCase(Email.INBOX))
@ -251,13 +288,14 @@ public class ImapStore extends Store
} }
else else
{ {
if (mPathPrefix.length() > 0)
if (getCombinedPrefix().length() > 0)
{ {
if (folder.length() >= mPathPrefix.length() + 1) if (folder.length() >= getCombinedPrefix().length())
{ {
folder = folder.substring(mPathPrefix.length() + 1); folder = folder.substring(getCombinedPrefix().length());
} }
if (!decodeFolderName(response.getString(3)).equals(mPathPrefix + mPathDelimeter + folder)) if (!decodeFolderName(response.getString(3)).equals(getCombinedPrefix() + folder))
{ {
includeFolder = false; includeFolder = false;
} }
@ -406,11 +444,6 @@ public class ImapStore extends Store
return true; return true;
} }
@Override
public Pusher getPusher(PushReceiver receiver, List<String> names)
{
return new ImapPusher(this, receiver, names);
}
class ImapFolder extends Folder class ImapFolder extends Folder
{ {
@ -433,18 +466,7 @@ public class ImapStore extends Store
String prefixedName = ""; String prefixedName = "";
if (!Email.INBOX.equalsIgnoreCase(mName)) if (!Email.INBOX.equalsIgnoreCase(mName))
{ {
String prefix = mPathPrefix; prefixedName = getCombinedPrefix();
String delim = mPathDelimeter;
if (prefix != null && delim != null)
{
prefix = prefix.trim();
delim = delim.trim();
if (prefix.length() > 0 && delim.length() > 0)
{
prefixedName += mPathPrefix + mPathDelimeter;
}
}
} }
prefixedName += mName; prefixedName += mName;
@ -512,7 +534,7 @@ public class ImapStore extends Store
{ {
if (response.get(0).equals("LIST")) if (response.get(0).equals("LIST"))
{ {
mPathDelimeter = nameResponses.get(0).getString(2); mPathDelimeter = response.getString(2);
if (Email.DEBUG) if (Email.DEBUG)
{ {
Log.d(Email.LOG_TAG, "Got path delimeter '" + mPathDelimeter + "' for " + getLogId()); Log.d(Email.LOG_TAG, "Got path delimeter '" + mPathDelimeter + "' for " + getLogId());
@ -1169,23 +1191,8 @@ public class ImapStore extends Store
return responses; return responses;
} }
/** protected void handlePossibleUidNext(ImapResponse response)
* Handle an untagged response that the caller doesn't care to handle themselves.
* @param response
*/
protected void handleUntaggedResponse(ImapResponse response)
{ {
//Log.i(Email.LOG_TAG, "Got response with size " + response.size() + ": " + response);
if (response.mTag == null && response.size() > 1)
{
if (response.get(1).equals("EXISTS"))
{
mMessageCount = response.getNumber(0);
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
}
}
if (response.get(0).equals("OK") && response.size() > 1) if (response.get(0).equals("OK") && response.size() > 1)
{ {
Object bracketedObj = response.get(1); Object bracketedObj = response.get(1);
@ -1213,7 +1220,28 @@ public class ImapStore extends Store
} }
} }
else if (response.get(1).equals("EXPUNGE") && mMessageCount > 0) }
/**
* Handle an untagged response that the caller doesn't care to handle themselves.
* @param response
*/
protected void handleUntaggedResponse(ImapResponse response)
{
//Log.i(Email.LOG_TAG, "Got response with size " + response.size() + ": " + response);
if (response.mTag == null && response.size() > 1)
{
if (response.get(1).equals("EXISTS"))
{
mMessageCount = response.getNumber(0);
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
}
}
handlePossibleUidNext(response);
if (response.get(1).equals("EXPUNGE") && mMessageCount > 0)
{ {
mMessageCount--; mMessageCount--;
if (Email.DEBUG) if (Email.DEBUG)
@ -1873,6 +1901,61 @@ public class ImapStore extends Store
{ {
throw new AuthenticationFailedException(null, me); throw new AuthenticationFailedException(null, me);
} }
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "NAMESPACE = " + hasCapability(CAPABILITY_NAMESPACE)
+ ", mPathPrefix = " + mPathPrefix);
}
if (mPathPrefix == null)
{
if (hasCapability(CAPABILITY_NAMESPACE))
{
Log.i(Email.LOG_TAG, "mPathPrefix is unset and server has NAMESPACE capability");
List<ImapResponse> namespaceResponses =
executeSimpleCommand(COMMAND_NAMESPACE);
for (ImapResponse response : namespaceResponses)
{
if (response.get(0).equals(COMMAND_NAMESPACE))
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got NAMESPACE response " + response + " on " + getLogId());
}
Object personalNamespaces = response.get(1);
if (personalNamespaces != null && personalNamespaces instanceof ImapList)
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got personal namespaces: " + personalNamespaces);
}
ImapList bracketed = (ImapList)personalNamespaces;
Object firstNamespace = bracketed.get(0);
if (firstNamespace != null && firstNamespace instanceof ImapList)
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got first personal namespaces: " + firstNamespace);
}
bracketed = (ImapList)firstNamespace;
mPathPrefix = bracketed.getString(0);
mPathDelimeter = bracketed.getString(1);
mCombinedPrefix = null;
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got path '" + mPathPrefix + "' and separator '" + mPathDelimeter + "'");
}
}
}
}
}
}
else
{
Log.i(Email.LOG_TAG, "mPathPrefix is unset but server does not have NAMESPACE capability");
mPathPrefix = "";
}
}
} }
catch (SSLException e) catch (SSLException e)
{ {
@ -1922,7 +2005,12 @@ public class ImapStore extends Store
// Log.v(Email.LOG_TAG, "Have capability '" + capability + "' for " + getLogId()); // Log.v(Email.LOG_TAG, "Have capability '" + capability + "' for " + getLogId());
// } // }
} }
return capabilities.contains("IDLE"); return capabilities.contains(CAPABILITY_IDLE);
}
protected boolean hasCapability(String capability)
{
return capabilities.contains(capability);
} }
private boolean isOpen() private boolean isOpen()
@ -2287,7 +2375,7 @@ public class ImapStore extends Store
} }
} }
public void start() throws MessagingException public void start()
{ {
Runnable runner = new Runnable() Runnable runner = new Runnable()
{ {
@ -2297,7 +2385,6 @@ public class ImapStore extends Store
Log.i(Email.LOG_TAG, "Pusher starting for " + getLogId()); Log.i(Email.LOG_TAG, "Pusher starting for " + getLogId());
while (stop.get() != true) while (stop.get() != true)
{ {
try try
{ {
int oldUidNext = -1; int oldUidNext = -1;
@ -2332,17 +2419,17 @@ public class ImapStore extends Store
{ {
handleUntaggedResponses(responses); handleUntaggedResponses(responses);
} }
if (uidNext > oldUidNext)
{
int startUid = oldUidNext; int startUid = oldUidNext;
if (startUid < uidNext - 100) if (startUid < uidNext - 10)
{ {
startUid = uidNext - 100; startUid = uidNext - 10;
} }
if (startUid < 1) if (startUid < 1)
{ {
startUid = 1; startUid = 1;
} }
if (uidNext > startUid)
{
Log.i(Email.LOG_TAG, "Needs sync from uid " + startUid + " to " + uidNext + " for " + getLogId()); Log.i(Email.LOG_TAG, "Needs sync from uid " + startUid + " to " + uidNext + " for " + getLogId());
List<Message> messages = new ArrayList<Message>(); List<Message> messages = new ArrayList<Message>();
@ -2359,7 +2446,7 @@ public class ImapStore extends Store
} }
else else
{ {
if (stop.get() != true) if (stop.get() == false)
{ {
List<ImapResponse> untaggedResponses = null; List<ImapResponse> untaggedResponses = null;
if (storedUntaggedResponses.size() > 0) if (storedUntaggedResponses.size() > 0)
@ -2378,8 +2465,11 @@ public class ImapStore extends Store
idling.set(false); idling.set(false);
} }
if (stop.get() == false)
{
storedUntaggedResponses.clear(); storedUntaggedResponses.clear();
processUntaggedResponses(untaggedResponses); processUntaggedResponses(untaggedResponses);
}
delayTime.set(NORMAL_DELAY_TIME); delayTime.set(NORMAL_DELAY_TIME);
} }
} }
@ -2404,6 +2494,7 @@ public class ImapStore extends Store
} }
else else
{ {
receiver.pushError("Push error: " + e.getMessage(), null);
Log.e(Email.LOG_TAG, "Got exception while idling for " + getLogId(), e); Log.e(Email.LOG_TAG, "Got exception while idling for " + getLogId(), e);
int delayTimeInt = delayTime.get(); int delayTimeInt = delayTime.get();
receiver.sleep(delayTimeInt); receiver.sleep(delayTimeInt);
@ -2453,18 +2544,26 @@ public class ImapStore extends Store
} }
storedUntaggedResponses.add(response); storedUntaggedResponses.add(response);
} }
handlePossibleUidNext(response);
} }
} }
protected void processUntaggedResponses(List<ImapResponse> responses) protected void processUntaggedResponses(List<ImapResponse> responses)
{ {
boolean skipSync = false;
int oldMessageCount = mMessageCount; int oldMessageCount = mMessageCount;
if (oldMessageCount == -1)
{
skipSync = true;
}
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>(); List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
for (ImapResponse response : responses) for (ImapResponse response : responses)
{ {
oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs); oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs);
} }
if (skipSync == false)
{
if (oldMessageCount < 0) if (oldMessageCount < 0)
{ {
oldMessageCount = 0; oldMessageCount = 0;
@ -2473,6 +2572,7 @@ public class ImapStore extends Store
{ {
syncMessages(oldMessageCount + 1, mMessageCount, true); syncMessages(oldMessageCount + 1, mMessageCount, true);
} }
}
if (Email.DEBUG) if (Email.DEBUG)
{ {
Log.d(Email.LOG_TAG, "There are " + flagSyncMsgSeqs + " messages needing flag sync for " + getLogId()); Log.d(Email.LOG_TAG, "There are " + flagSyncMsgSeqs + " messages needing flag sync for " + getLogId());
@ -2610,10 +2710,13 @@ public class ImapStore extends Store
} }
} }
public void stop() throws MessagingException public void stop()
{ {
stop.set(true); stop.set(true);
if (listeningThread != null)
{
listeningThread.interrupt();
}
if (mConnection != null) if (mConnection != null)
{ {
if (Email.DEBUG) if (Email.DEBUG)
@ -2634,6 +2737,23 @@ public class ImapStore extends Store
{ {
Log.v(Email.LOG_TAG, "Got async response: " + response); Log.v(Email.LOG_TAG, "Got async response: " + response);
} }
if (stop.get() == true)
{
if (Email.DEBUG)
{
Log.d(Email.LOG_TAG, "Got async untagged response: " + response + ", but stop is set for " + getLogId());
}
try
{
sendDone();
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
}
}
else
{
if (response.mTag == null) if (response.mTag == null)
{ {
if (response.size() > 1) if (response.size() > 1)
@ -2676,23 +2796,51 @@ public class ImapStore extends Store
} }
} }
} }
}
@Override
public Pusher getPusher(PushReceiver receiver)
{
return new ImapPusher(this, receiver);
}
public class ImapPusher implements Pusher public class ImapPusher implements Pusher
{ {
List<ImapFolderPusher> folderPushers = new ArrayList<ImapFolderPusher>(); final ImapStore mStore;
final PushReceiver mReceiver;
public ImapPusher(ImapStore store, PushReceiver receiver, List<String> folderNames) HashMap<String, ImapFolderPusher> folderPushers = new HashMap<String, ImapFolderPusher>();
public ImapPusher(ImapStore store, PushReceiver receiver)
{
mStore = store;
mReceiver = receiver;
}
@Override
public void start(List<String> folderNames)
{
stop();
synchronized(folderPushers)
{ {
for (String folderName : folderNames) for (String folderName : folderNames)
{ {
ImapFolderPusher pusher = new ImapFolderPusher(store, folderName, receiver); ImapFolderPusher pusher = folderPushers.get(folderName);
folderPushers.add(pusher); if (pusher == null)
{
pusher = new ImapFolderPusher(mStore, folderName, mReceiver);
folderPushers.put(folderName, pusher);
pusher.start();
}
}
} }
} }
@Override
public void refresh() public void refresh()
{ {
for (ImapFolderPusher folderPusher : folderPushers) synchronized(folderPushers)
{
for (ImapFolderPusher folderPusher : folderPushers.values())
{ {
try try
{ {
@ -2703,28 +2851,16 @@ public class ImapStore extends Store
Log.e(Email.LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e); Log.e(Email.LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e);
} }
} }
}
public void start()
{
for (ImapFolderPusher folderPusher : folderPushers)
{
try
{
folderPusher.start();
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Got exception while starting " + folderPusher.getName(), e);
}
} }
} }
@Override
public void stop() public void stop()
{ {
Log.i(Email.LOG_TAG, "Requested stop of IMAP pusher"); Log.i(Email.LOG_TAG, "Requested stop of IMAP pusher");
for (ImapFolderPusher folderPusher : folderPushers) synchronized(folderPushers)
{
for (ImapFolderPusher folderPusher : folderPushers.values())
{ {
try try
{ {
@ -2738,6 +2874,7 @@ public class ImapStore extends Store
} }
folderPushers.clear(); folderPushers.clear();
} }
}
public int getRefreshInterval() public int getRefreshInterval()
{ {

View File

@ -69,8 +69,8 @@ public class BootReceiver extends BroadcastReceiver
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()))
{ {
Email.setServicesEnabled(context, tmpWakeLockId); //Email.setServicesEnabled(context, tmpWakeLockId);
tmpWakeLockId = null; //tmpWakeLockId = null;
} }
else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction())) else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction()))
{ {

View File

@ -361,13 +361,7 @@ public class MailService extends CoreService
for (Account account : Preferences.getPreferences(MailService.this).getAccounts()) for (Account account : Preferences.getPreferences(MailService.this).getAccounts())
{ {
Log.i(Email.LOG_TAG, "Setting up pushers for account " + account.getDescription()); Log.i(Email.LOG_TAG, "Setting up pushers for account " + account.getDescription());
Pusher pusher = MessagingController.getInstance(getApplication()).setupPushing(account); pushing |= MessagingController.getInstance(getApplication()).setupPushing(account);
if (pusher != null)
{
pushing = true;
Log.i(Email.LOG_TAG, "Starting configured pusher for account " + account.getDescription());
pusher.start();
}
} }
if (pushing) if (pushing)
{ {