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:
parent
b18e88cf0d
commit
d871d2d2ee
9 changed files with 482 additions and 314 deletions
|
@ -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_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_sent">Sent folder name</string>
|
||||
|
|
|
@ -831,6 +831,12 @@ public class Account implements Serializable
|
|||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return mUuid.hashCode();
|
||||
}
|
||||
|
||||
public FolderMode getFolderDisplayMode()
|
||||
{
|
||||
|
|
|
@ -3995,147 +3995,15 @@ public class MessagingController implements Runnable
|
|||
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)
|
||||
Pusher previousPusher = pushers.remove(account);
|
||||
if (previousPusher != null)
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Account " + account.getDescription() + " is not push capable, skipping");
|
||||
return null;
|
||||
previousPusher.stop();
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
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 : getListeners())
|
||||
{
|
||||
l.setPushActive(account, folderName, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
try
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(mApplication);
|
||||
|
||||
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());
|
||||
names.add(folder.getName());
|
||||
}
|
||||
|
||||
if (names.size() > 0)
|
||||
{
|
||||
pusher = store.getPusher(receiver, names);
|
||||
if (pusher != null)
|
||||
PushReceiver receiver = new MessagingControllerPushReceiver(mApplication, account, this);
|
||||
|
||||
try
|
||||
{
|
||||
pushers.put(account, pusher);
|
||||
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)
|
||||
{
|
||||
oldPusher = pushers.putIfAbsent(account, pusher);
|
||||
}
|
||||
if (oldPusher != null)
|
||||
{
|
||||
pusher = oldPusher;
|
||||
}
|
||||
else
|
||||
{
|
||||
pusher.start(names);
|
||||
}
|
||||
}
|
||||
return pusher;
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Could not get remote store", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void stopPushing(Account account)
|
||||
{
|
||||
Pusher pusher = pushers.remove(account);
|
||||
if (pusher != null)
|
||||
{
|
||||
pusher.stop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void stopAllPushing()
|
||||
|
@ -4220,7 +4106,6 @@ public class MessagingController implements Runnable
|
|||
iter.remove();
|
||||
pusher.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
localFolder= (LocalFolder) localStore.getFolder(remoteFolder.getName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
remoteFolder.open(OpenMode.READ_WRITE);
|
||||
|
||||
int newCount = downloadMessages(account, remoteFolder, localFolder, messages, flagSyncOnly);
|
||||
setLocalUnreadCountToRemote(localFolder, remoteFolder, messages.size());
|
||||
|
|
145
src/com/android/email/MessagingControllerPushReceiver.java
Normal file
145
src/com/android/email/MessagingControllerPushReceiver.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package com.android.email.mail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface Pusher
|
||||
{
|
||||
public void start();
|
||||
public void start(List<String> folderNames);
|
||||
public void refresh();
|
||||
public void stop();
|
||||
/**
|
||||
|
|
|
@ -113,7 +113,7 @@ public abstract class Store
|
|||
{
|
||||
}
|
||||
|
||||
public Pusher getPusher(PushReceiver receiver, List<String> names)
|
||||
public Pusher getPusher(PushReceiver receiver)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -99,14 +100,20 @@ public class ImapStore extends Store
|
|||
private static final int IDLE_REFRESH_INTERVAL = 20 * 60 * 1000; // 20 minutes
|
||||
|
||||
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 int mPort;
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private int mConnectionSecurity;
|
||||
private String mPathPrefix;
|
||||
private String mPathDelimeter;
|
||||
private volatile String mPathPrefix;
|
||||
private volatile String mCombinedPrefix = null;
|
||||
private volatile String mPathDelimeter;
|
||||
|
||||
private LinkedList<ImapConnection> mConnections =
|
||||
new LinkedList<ImapConnection>();
|
||||
|
@ -196,6 +203,10 @@ public class ImapStore extends Store
|
|||
if ((uri.getPath() != null) && (uri.getPath().length() > 0))
|
||||
{
|
||||
mPathPrefix = uri.getPath().substring(1);
|
||||
if (mPathPrefix != null && mPathPrefix.trim().length() == 0)
|
||||
{
|
||||
mPathPrefix = null;
|
||||
}
|
||||
}
|
||||
|
||||
mModifiedUtf7Charset = new CharsetProvider().charsetForName("X-RFC-3501");
|
||||
|
@ -217,6 +228,34 @@ public class ImapStore extends Store
|
|||
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
|
||||
public Folder[] getPersonalNamespaces() throws MessagingException
|
||||
|
@ -225,13 +264,10 @@ public class ImapStore extends Store
|
|||
try
|
||||
{
|
||||
ArrayList<Folder> folders = new ArrayList<Folder>();
|
||||
if (mPathPrefix == null)
|
||||
{
|
||||
mPathPrefix = "";
|
||||
}
|
||||
|
||||
List<ImapResponse> responses =
|
||||
connection.executeSimpleCommand(String.format("LIST \"\" \"%s*\"",
|
||||
mPathPrefix));
|
||||
getCombinedPrefix()));
|
||||
|
||||
for (ImapResponse response : responses)
|
||||
{
|
||||
|
@ -243,6 +279,7 @@ public class ImapStore extends Store
|
|||
if (mPathDelimeter == null)
|
||||
{
|
||||
mPathDelimeter = response.getString(2);
|
||||
mCombinedPrefix = null;
|
||||
}
|
||||
|
||||
if (folder.equalsIgnoreCase(Email.INBOX))
|
||||
|
@ -251,13 +288,14 @@ public class ImapStore extends Store
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
@ -406,12 +444,7 @@ public class ImapStore extends Store
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pusher getPusher(PushReceiver receiver, List<String> names)
|
||||
{
|
||||
return new ImapPusher(this, receiver, names);
|
||||
}
|
||||
|
||||
|
||||
class ImapFolder extends Folder
|
||||
{
|
||||
private String mName;
|
||||
|
@ -433,18 +466,7 @@ public class ImapStore extends Store
|
|||
String prefixedName = "";
|
||||
if (!Email.INBOX.equalsIgnoreCase(mName))
|
||||
{
|
||||
String prefix = mPathPrefix;
|
||||
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 = getCombinedPrefix();
|
||||
}
|
||||
|
||||
prefixedName += mName;
|
||||
|
@ -503,7 +525,7 @@ public class ImapStore extends Store
|
|||
// 2 OK [READ-WRITE] Select completed.
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (mPathDelimeter == null)
|
||||
{
|
||||
List<ImapResponse> nameResponses =
|
||||
|
@ -512,7 +534,7 @@ public class ImapStore extends Store
|
|||
{
|
||||
if (response.get(0).equals("LIST"))
|
||||
{
|
||||
mPathDelimeter = nameResponses.get(0).getString(2);
|
||||
mPathDelimeter = response.getString(2);
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got path delimeter '" + mPathDelimeter + "' for " + getLogId());
|
||||
|
@ -1168,6 +1190,37 @@ public class ImapStore extends Store
|
|||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
protected void handlePossibleUidNext(ImapResponse response)
|
||||
{
|
||||
if (response.get(0).equals("OK") && response.size() > 1)
|
||||
{
|
||||
Object bracketedObj = response.get(1);
|
||||
if (bracketedObj instanceof ImapList)
|
||||
{
|
||||
ImapList bracketed = (ImapList)bracketedObj;
|
||||
|
||||
if (bracketed.size() > 1)
|
||||
{
|
||||
Object keyObj = bracketed.get(0);
|
||||
if (keyObj instanceof String)
|
||||
{
|
||||
String key = (String)keyObj;
|
||||
if ("UIDNEXT".equals(key))
|
||||
{
|
||||
uidNext = bracketed.getNumber(1);
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an untagged response that the caller doesn't care to handle themselves.
|
||||
|
@ -1186,34 +1239,9 @@ public class ImapStore extends Store
|
|||
Log.d(Email.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
|
||||
}
|
||||
}
|
||||
if (response.get(0).equals("OK") && response.size() > 1)
|
||||
{
|
||||
Object bracketedObj = response.get(1);
|
||||
if (bracketedObj instanceof ImapList)
|
||||
{
|
||||
ImapList bracketed = (ImapList)bracketedObj;
|
||||
|
||||
if (bracketed.size() > 1)
|
||||
{
|
||||
Object keyObj = bracketed.get(0);
|
||||
if (keyObj instanceof String)
|
||||
{
|
||||
String key = (String)keyObj;
|
||||
if ("UIDNEXT".equals(key))
|
||||
{
|
||||
uidNext = bracketed.getNumber(1);
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else if (response.get(1).equals("EXPUNGE") && mMessageCount > 0)
|
||||
handlePossibleUidNext(response);
|
||||
|
||||
if (response.get(1).equals("EXPUNGE") && mMessageCount > 0)
|
||||
{
|
||||
mMessageCount--;
|
||||
if (Email.DEBUG)
|
||||
|
@ -1873,6 +1901,61 @@ public class ImapStore extends Store
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
@ -1922,7 +2005,12 @@ public class ImapStore extends Store
|
|||
// 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()
|
||||
|
@ -2287,7 +2375,7 @@ public class ImapStore extends Store
|
|||
}
|
||||
}
|
||||
|
||||
public void start() throws MessagingException
|
||||
public void start()
|
||||
{
|
||||
Runnable runner = new Runnable()
|
||||
{
|
||||
|
@ -2297,7 +2385,6 @@ public class ImapStore extends Store
|
|||
Log.i(Email.LOG_TAG, "Pusher starting for " + getLogId());
|
||||
while (stop.get() != true)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
int oldUidNext = -1;
|
||||
|
@ -2332,17 +2419,17 @@ public class ImapStore extends Store
|
|||
{
|
||||
handleUntaggedResponses(responses);
|
||||
}
|
||||
if (uidNext > oldUidNext)
|
||||
int startUid = oldUidNext;
|
||||
if (startUid < uidNext - 10)
|
||||
{
|
||||
startUid = uidNext - 10;
|
||||
}
|
||||
if (startUid < 1)
|
||||
{
|
||||
startUid = 1;
|
||||
}
|
||||
if (uidNext > startUid)
|
||||
{
|
||||
int startUid = oldUidNext;
|
||||
if (startUid < uidNext - 100)
|
||||
{
|
||||
startUid = uidNext - 100;
|
||||
}
|
||||
if (startUid < 1)
|
||||
{
|
||||
startUid = 1;
|
||||
}
|
||||
|
||||
Log.i(Email.LOG_TAG, "Needs sync from uid " + startUid + " to " + uidNext + " for " + getLogId());
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
|
@ -2359,7 +2446,7 @@ public class ImapStore extends Store
|
|||
}
|
||||
else
|
||||
{
|
||||
if (stop.get() != true)
|
||||
if (stop.get() == false)
|
||||
{
|
||||
List<ImapResponse> untaggedResponses = null;
|
||||
if (storedUntaggedResponses.size() > 0)
|
||||
|
@ -2378,8 +2465,11 @@ public class ImapStore extends Store
|
|||
idling.set(false);
|
||||
|
||||
}
|
||||
storedUntaggedResponses.clear();
|
||||
processUntaggedResponses(untaggedResponses);
|
||||
if (stop.get() == false)
|
||||
{
|
||||
storedUntaggedResponses.clear();
|
||||
processUntaggedResponses(untaggedResponses);
|
||||
}
|
||||
delayTime.set(NORMAL_DELAY_TIME);
|
||||
}
|
||||
}
|
||||
|
@ -2404,6 +2494,7 @@ public class ImapStore extends Store
|
|||
}
|
||||
else
|
||||
{
|
||||
receiver.pushError("Push error: " + e.getMessage(), null);
|
||||
Log.e(Email.LOG_TAG, "Got exception while idling for " + getLogId(), e);
|
||||
int delayTimeInt = delayTime.get();
|
||||
receiver.sleep(delayTimeInt);
|
||||
|
@ -2453,25 +2544,34 @@ public class ImapStore extends Store
|
|||
}
|
||||
storedUntaggedResponses.add(response);
|
||||
}
|
||||
handlePossibleUidNext(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected void processUntaggedResponses(List<ImapResponse> responses)
|
||||
{
|
||||
boolean skipSync = false;
|
||||
int oldMessageCount = mMessageCount;
|
||||
if (oldMessageCount == -1)
|
||||
{
|
||||
skipSync = true;
|
||||
}
|
||||
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
|
||||
|
||||
for (ImapResponse response : responses)
|
||||
{
|
||||
oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs);
|
||||
}
|
||||
if (oldMessageCount < 0)
|
||||
if (skipSync == false)
|
||||
{
|
||||
oldMessageCount = 0;
|
||||
}
|
||||
if (mMessageCount > oldMessageCount)
|
||||
{
|
||||
syncMessages(oldMessageCount + 1, mMessageCount, true);
|
||||
if (oldMessageCount < 0)
|
||||
{
|
||||
oldMessageCount = 0;
|
||||
}
|
||||
if (mMessageCount > oldMessageCount)
|
||||
{
|
||||
syncMessages(oldMessageCount + 1, mMessageCount, true);
|
||||
}
|
||||
}
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
|
@ -2610,10 +2710,13 @@ public class ImapStore extends Store
|
|||
}
|
||||
}
|
||||
|
||||
public void stop() throws MessagingException
|
||||
public void stop()
|
||||
{
|
||||
stop.set(true);
|
||||
|
||||
if (listeningThread != null)
|
||||
{
|
||||
listeningThread.interrupt();
|
||||
}
|
||||
if (mConnection != null)
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
|
@ -2634,109 +2737,143 @@ public class ImapStore extends Store
|
|||
{
|
||||
Log.v(Email.LOG_TAG, "Got async response: " + response);
|
||||
}
|
||||
if (response.mTag == null)
|
||||
if (stop.get() == true)
|
||||
{
|
||||
if (response.size() > 1)
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
boolean started = false;
|
||||
Object responseType = response.get(1);
|
||||
if ("EXISTS".equals(responseType) || "EXPUNGE".equals(responseType) ||
|
||||
"FETCH".equals(responseType))
|
||||
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.size() > 1)
|
||||
{
|
||||
if (started == false)
|
||||
boolean started = false;
|
||||
Object responseType = response.get(1);
|
||||
if ("EXISTS".equals(responseType) || "EXPUNGE".equals(responseType) ||
|
||||
"FETCH".equals(responseType))
|
||||
{
|
||||
receiver.acquireWakeLock();
|
||||
started = true;
|
||||
}
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got useful async untagged response: " + response + " for " + getLogId());
|
||||
}
|
||||
try
|
||||
{
|
||||
sendDone();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
|
||||
if (started == false)
|
||||
{
|
||||
receiver.acquireWakeLock();
|
||||
started = true;
|
||||
}
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Got useful async untagged response: " + response + " for " + getLogId());
|
||||
}
|
||||
try
|
||||
{
|
||||
sendDone();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (response.size() > 0)
|
||||
{
|
||||
if ("idling".equals(response.get(0)))
|
||||
else if (response.size() > 0)
|
||||
{
|
||||
if (Email.DEBUG)
|
||||
if ("idling".equals(response.get(0)))
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Idling " + getLogId());
|
||||
if (Email.DEBUG)
|
||||
{
|
||||
Log.d(Email.LOG_TAG, "Idling " + getLogId());
|
||||
}
|
||||
receiver.releaseWakeLock();
|
||||
}
|
||||
receiver.releaseWakeLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Pusher getPusher(PushReceiver receiver)
|
||||
{
|
||||
return new ImapPusher(this, receiver);
|
||||
}
|
||||
|
||||
public class ImapPusher implements Pusher
|
||||
{
|
||||
List<ImapFolderPusher> folderPushers = new ArrayList<ImapFolderPusher>();
|
||||
final ImapStore mStore;
|
||||
final PushReceiver mReceiver;
|
||||
|
||||
HashMap<String, ImapFolderPusher> folderPushers = new HashMap<String, ImapFolderPusher>();
|
||||
|
||||
public ImapPusher(ImapStore store, PushReceiver receiver, List<String> folderNames)
|
||||
public ImapPusher(ImapStore store, PushReceiver receiver)
|
||||
{
|
||||
for (String folderName : folderNames)
|
||||
mStore = store;
|
||||
mReceiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(List<String> folderNames)
|
||||
{
|
||||
stop();
|
||||
synchronized(folderPushers)
|
||||
{
|
||||
ImapFolderPusher pusher = new ImapFolderPusher(store, folderName, receiver);
|
||||
folderPushers.add(pusher);
|
||||
for (String folderName : folderNames)
|
||||
{
|
||||
ImapFolderPusher pusher = folderPushers.get(folderName);
|
||||
if (pusher == null)
|
||||
{
|
||||
pusher = new ImapFolderPusher(mStore, folderName, mReceiver);
|
||||
folderPushers.put(folderName, pusher);
|
||||
pusher.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh()
|
||||
{
|
||||
for (ImapFolderPusher folderPusher : folderPushers)
|
||||
synchronized(folderPushers)
|
||||
{
|
||||
try
|
||||
for (ImapFolderPusher folderPusher : folderPushers.values())
|
||||
{
|
||||
folderPusher.refresh();
|
||||
}
|
||||
catch (Exception 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);
|
||||
try
|
||||
{
|
||||
folderPusher.refresh();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Requested stop of IMAP pusher");
|
||||
for (ImapFolderPusher folderPusher : folderPushers)
|
||||
synchronized(folderPushers)
|
||||
{
|
||||
try
|
||||
for (ImapFolderPusher folderPusher : folderPushers.values())
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Requesting stop of IMAP folderPusher " + folderPusher.getName());
|
||||
folderPusher.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Got exception while stopping " + folderPusher.getName(), e);
|
||||
try
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Requesting stop of IMAP folderPusher " + folderPusher.getName());
|
||||
folderPusher.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(Email.LOG_TAG, "Got exception while stopping " + folderPusher.getName(), e);
|
||||
}
|
||||
}
|
||||
folderPushers.clear();
|
||||
}
|
||||
folderPushers.clear();
|
||||
}
|
||||
|
||||
public int getRefreshInterval()
|
||||
|
|
|
@ -69,8 +69,8 @@ public class BootReceiver extends BroadcastReceiver
|
|||
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()))
|
||||
{
|
||||
Email.setServicesEnabled(context, tmpWakeLockId);
|
||||
tmpWakeLockId = null;
|
||||
//Email.setServicesEnabled(context, tmpWakeLockId);
|
||||
//tmpWakeLockId = null;
|
||||
}
|
||||
else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction()))
|
||||
{
|
||||
|
|
|
@ -361,13 +361,7 @@ public class MailService extends CoreService
|
|||
for (Account account : Preferences.getPreferences(MailService.this).getAccounts())
|
||||
{
|
||||
Log.i(Email.LOG_TAG, "Setting up pushers for account " + account.getDescription());
|
||||
Pusher pusher = MessagingController.getInstance(getApplication()).setupPushing(account);
|
||||
if (pusher != null)
|
||||
{
|
||||
pushing = true;
|
||||
Log.i(Email.LOG_TAG, "Starting configured pusher for account " + account.getDescription());
|
||||
pusher.start();
|
||||
}
|
||||
pushing |= MessagingController.getInstance(getApplication()).setupPushing(account);
|
||||
}
|
||||
if (pushing)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue