Remove old Push code
This commit is contained in:
parent
3f60e41155
commit
2a78418911
24 changed files with 0 additions and 1700 deletions
|
@ -7,14 +7,12 @@ import java.util.Collections;
|
|||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
@ -69,8 +67,6 @@ import com.fsck.k9.mail.Message.RecipientType;
|
|||
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mailstore.LocalFolder;
|
||||
|
@ -127,7 +123,6 @@ public class MessagingController {
|
|||
|
||||
private final BlockingQueue<Command> queuedCommands = new PriorityBlockingQueue<>();
|
||||
private final Set<MessagingListener> listeners = new CopyOnWriteArraySet<>();
|
||||
private final ConcurrentHashMap<Account, Pusher> pushers = new ConcurrentHashMap<>();
|
||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
private final MemorizingMessagingListener memorizingMessagingListener = new MemorizingMessagingListener();
|
||||
private final UnreadMessageCountProvider unreadMessageCountProvider;
|
||||
|
@ -2657,116 +2652,6 @@ public class MessagingController {
|
|||
}
|
||||
}
|
||||
|
||||
public Collection<Pusher> getPushers() {
|
||||
return pushers.values();
|
||||
}
|
||||
|
||||
public Pusher getPusher(Account account) {
|
||||
return pushers.get(account);
|
||||
}
|
||||
|
||||
public boolean setupPushing(final Account account) {
|
||||
try {
|
||||
Pusher previousPusher = pushers.remove(account);
|
||||
if (previousPusher != null) {
|
||||
previousPusher.stop();
|
||||
}
|
||||
|
||||
Account.FolderMode aDisplayMode = account.getFolderDisplayMode();
|
||||
Account.FolderMode aPushMode = account.getFolderPushMode();
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
|
||||
LocalStore localStore = localStoreProvider.getInstance(account);
|
||||
for (final LocalFolder folder : localStore.getPersonalNamespaces(false)) {
|
||||
if (folder.getServerId().equals(account.getOutboxFolder())) {
|
||||
continue;
|
||||
}
|
||||
folder.open();
|
||||
|
||||
FolderClass fDisplayClass = folder.getDisplayClass();
|
||||
FolderClass fPushClass = folder.getPushClass();
|
||||
|
||||
if (LocalFolder.isModeMismatch(aDisplayMode, fDisplayClass)) {
|
||||
// Never push a folder that isn't displayed
|
||||
/*
|
||||
if (K9.DEBUG) {
|
||||
Log.v(K9.LOG_TAG, "Not pushing folder " + folder.getName() +
|
||||
" which is in display class " + fDisplayClass + " while account is in display mode " + aDisplayMode);
|
||||
}
|
||||
*/
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LocalFolder.isModeMismatch(aPushMode, fPushClass)) {
|
||||
// Do not push folders in the wrong class
|
||||
/*
|
||||
if (K9.DEBUG) {
|
||||
Log.v(K9.LOG_TAG, "Not pushing folder " + folder.getName() +
|
||||
" which is in push mode " + fPushClass + " while account is in push mode " + aPushMode);
|
||||
}
|
||||
*/
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Timber.i("Starting pusher for %s:%s", account.getDescription(), folder.getServerId());
|
||||
|
||||
names.add(folder.getServerId());
|
||||
}
|
||||
|
||||
if (!names.isEmpty()) {
|
||||
PushReceiver receiver = new MessagingControllerPushReceiver(context, localStoreProvider, account, this);
|
||||
int maxPushFolders = account.getMaxPushFolders();
|
||||
|
||||
if (names.size() > maxPushFolders) {
|
||||
Timber.i("Count of folders to push for account %s is %d, greater than limit of %d, truncating",
|
||||
account.getDescription(), names.size(), maxPushFolders);
|
||||
|
||||
names = names.subList(0, maxPushFolders);
|
||||
}
|
||||
|
||||
try {
|
||||
Backend backend = getBackend(account);
|
||||
if (!backend.isPushCapable()) {
|
||||
Timber.i("Account %s is not push capable, skipping", account.getDescription());
|
||||
|
||||
return false;
|
||||
}
|
||||
Pusher pusher = backend.createPusher(receiver);
|
||||
Pusher oldPusher = pushers.putIfAbsent(account, pusher);
|
||||
if (oldPusher == null) {
|
||||
pusher.start(names);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Could not get remote store");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Timber.i("No folders are configured for pushing in account %s", account.getDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Got exception while setting up pushing");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void stopAllPushing() {
|
||||
Timber.i("Stopping all pushers");
|
||||
|
||||
Iterator<Pusher> iter = pushers.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Pusher pusher = iter.next();
|
||||
iter.remove();
|
||||
pusher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelNotificationsForAccount(Account account) {
|
||||
notificationController.clearNewMailNotifications(account);
|
||||
}
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
package com.fsck.k9.controller;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.power.WakeLock;
|
||||
import com.fsck.k9.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalStore;
|
||||
import com.fsck.k9.mailstore.LocalStoreProvider;
|
||||
import com.fsck.k9.service.SleepService;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class MessagingControllerPushReceiver implements PushReceiver {
|
||||
final Account account;
|
||||
final MessagingController controller;
|
||||
final Context context;
|
||||
final LocalStoreProvider localStoreProvider;
|
||||
|
||||
public MessagingControllerPushReceiver(Context context, LocalStoreProvider localStoreProvider,
|
||||
Account nAccount, MessagingController nController) {
|
||||
account = nAccount;
|
||||
controller = nController;
|
||||
this.localStoreProvider = localStoreProvider;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void messagesFlagsChanged(String folderServerId, List<Message> messages) {
|
||||
syncFolder(folderServerId);
|
||||
}
|
||||
public void messagesArrived(String folderServerId, List<Message> messages) {
|
||||
syncFolder(folderServerId);
|
||||
}
|
||||
public void messagesRemoved(String folderServerId, List<Message> messages) {
|
||||
syncFolder(folderServerId);
|
||||
}
|
||||
|
||||
public void syncFolder(String folderServerId) {
|
||||
Timber.v("syncFolder(%s)", folderServerId);
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
controller.synchronizeMailbox(account, folderServerId, new SimpleMessagingListener() {
|
||||
@Override
|
||||
public void synchronizeMailboxFinished(Account account, String folderServerId) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeMailboxFailed(Account account, String folderServerId,
|
||||
String message) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Timber.v("syncFolder(%s) about to await latch release", folderServerId);
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
Timber.v("syncFolder(%s) got latch release", folderServerId);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Interrupted while awaiting latch release");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleep(WakeLock wakeLock, long millis) {
|
||||
SleepService.sleep(context, millis, wakeLock, K9.PUSH_WAKE_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
public void pushError(String errorMessage, Exception e) {
|
||||
String errMess = errorMessage;
|
||||
|
||||
controller.notifyUserIfCertificateProblem(account, e, true);
|
||||
if (errMess == null && e != null) {
|
||||
errMess = e.getMessage();
|
||||
}
|
||||
Timber.e(e, errMess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationFailed() {
|
||||
controller.handleAuthenticationFailure(account, true);
|
||||
}
|
||||
|
||||
public String getPushState(String folderServerId) {
|
||||
LocalFolder localFolder = null;
|
||||
try {
|
||||
LocalStore localStore = localStoreProvider.getInstance(account);
|
||||
localFolder = localStore.getFolder(folderServerId);
|
||||
localFolder.open();
|
||||
return localFolder.getPushState();
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Unable to get push state from account %s, folder %s", account.getDescription(), folderServerId);
|
||||
return null;
|
||||
} finally {
|
||||
if (localFolder != null) {
|
||||
localFolder.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPushActive(String folderServerId, boolean enabled) {
|
||||
// Nothing to do for now
|
||||
}
|
||||
}
|
|
@ -115,14 +115,6 @@ class K9BackendFolder(
|
|||
database.setString(column = "status", value = status)
|
||||
}
|
||||
|
||||
override fun getPushState(): String? {
|
||||
return database.getString(column = "push_state")
|
||||
}
|
||||
|
||||
override fun setPushState(pushState: String?) {
|
||||
return database.setString(column = "push_state", value = pushState)
|
||||
}
|
||||
|
||||
override fun isMessagePresent(messageServerId: String): Boolean {
|
||||
return database.execute(false) { db ->
|
||||
val cursor = db.query(
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package com.fsck.k9.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class PushService extends CoreService {
|
||||
private static final String START_SERVICE = "com.fsck.k9.service.PushService.startService";
|
||||
private static final String STOP_SERVICE = "com.fsck.k9.service.PushService.stopService";
|
||||
|
||||
public static void startService(Context context) {
|
||||
Intent i = new Intent();
|
||||
i.setClass(context, PushService.class);
|
||||
i.setAction(PushService.START_SERVICE);
|
||||
addWakeLock(context, i);
|
||||
context.startService(i);
|
||||
}
|
||||
|
||||
public static void stopService(Context context) {
|
||||
Intent i = new Intent();
|
||||
i.setClass(context, PushService.class);
|
||||
i.setAction(PushService.STOP_SERVICE);
|
||||
addWakeLock(context, i);
|
||||
context.startService(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int startService(Intent intent, int startId) {
|
||||
int startFlag = START_STICKY;
|
||||
if (START_SERVICE.equals(intent.getAction())) {
|
||||
Timber.i("PushService started with startId = %d", startId);
|
||||
} else if (STOP_SERVICE.equals(intent.getAction())) {
|
||||
Timber.i("PushService stopping with startId = %d", startId);
|
||||
stopSelf(startId);
|
||||
startFlag = START_NOT_STICKY;
|
||||
}
|
||||
|
||||
return startFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setAutoShutdown(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package com.fsck.k9.service;
|
||||
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.fsck.k9.mail.power.WakeLock;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static java.lang.Thread.currentThread;
|
||||
|
||||
|
||||
public class SleepService extends CoreService {
|
||||
|
||||
private static final String ALARM_FIRED = "com.fsck.k9.service.SleepService.ALARM_FIRED";
|
||||
private static final String LATCH_ID = "com.fsck.k9.service.SleepService.LATCH_ID_EXTRA";
|
||||
|
||||
|
||||
private static ConcurrentHashMap<Integer, SleepDatum> sleepData = new ConcurrentHashMap<>();
|
||||
|
||||
private static AtomicInteger latchId = new AtomicInteger();
|
||||
|
||||
public static void sleep(Context context, long sleepTime, WakeLock wakeLock, long wakeLockTimeout) {
|
||||
Integer id = latchId.getAndIncrement();
|
||||
Timber.d("SleepService Preparing CountDownLatch with id = %d, thread %s", id, currentThread().getName());
|
||||
|
||||
SleepDatum sleepDatum = new SleepDatum();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
sleepDatum.latch = latch;
|
||||
sleepDatum.reacquireLatch = new CountDownLatch(1);
|
||||
sleepData.put(id, sleepDatum);
|
||||
|
||||
Intent i = new Intent(context, SleepService.class);
|
||||
i.putExtra(LATCH_ID, id);
|
||||
i.setAction(ALARM_FIRED + "." + id);
|
||||
long startTime = SystemClock.elapsedRealtime();
|
||||
long nextTime = startTime + sleepTime;
|
||||
BootReceiver.scheduleIntent(context, nextTime, i);
|
||||
if (wakeLock != null) {
|
||||
sleepDatum.wakeLock = wakeLock;
|
||||
sleepDatum.timeout = wakeLockTimeout;
|
||||
wakeLock.release();
|
||||
}
|
||||
try {
|
||||
boolean countedDown = latch.await(sleepTime, TimeUnit.MILLISECONDS);
|
||||
if (!countedDown) {
|
||||
Timber.d("SleepService latch timed out for id = %d, thread %s", id, currentThread().getName());
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
Timber.e(ie, "SleepService Interrupted while awaiting latch");
|
||||
}
|
||||
SleepDatum releaseDatum = sleepData.remove(id);
|
||||
if (releaseDatum == null) {
|
||||
try {
|
||||
Timber.d("SleepService waiting for reacquireLatch for id = %d, thread %s",
|
||||
id, currentThread().getName());
|
||||
|
||||
if (!sleepDatum.reacquireLatch.await(5000, TimeUnit.MILLISECONDS)) {
|
||||
Timber.w("SleepService reacquireLatch timed out for id = %d, thread %s",
|
||||
id, currentThread().getName());
|
||||
} else {
|
||||
Timber.d("SleepService reacquireLatch finished for id = %d, thread %s",
|
||||
id, currentThread().getName());
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
Timber.e(ie, "SleepService Interrupted while awaiting reacquireLatch");
|
||||
}
|
||||
} else {
|
||||
reacquireWakeLock(releaseDatum);
|
||||
}
|
||||
|
||||
long endTime = SystemClock.elapsedRealtime();
|
||||
long actualSleep = endTime - startTime;
|
||||
|
||||
if (actualSleep < sleepTime) {
|
||||
Timber.w("SleepService sleep time too short: requested was %d, actual was %d", sleepTime, actualSleep);
|
||||
} else {
|
||||
Timber.d("SleepService requested sleep time was %d, actual was %d", sleepTime, actualSleep);
|
||||
}
|
||||
}
|
||||
|
||||
private static void endSleep(Integer id) {
|
||||
if (id != -1) {
|
||||
SleepDatum sleepDatum = sleepData.remove(id);
|
||||
if (sleepDatum != null) {
|
||||
CountDownLatch latch = sleepDatum.latch;
|
||||
if (latch == null) {
|
||||
Timber.e("SleepService No CountDownLatch available with id = %s", id);
|
||||
} else {
|
||||
Timber.d("SleepService Counting down CountDownLatch with id = %d", id);
|
||||
latch.countDown();
|
||||
}
|
||||
reacquireWakeLock(sleepDatum);
|
||||
sleepDatum.reacquireLatch.countDown();
|
||||
} else {
|
||||
Timber.d("SleepService Sleep for id %d already finished", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void reacquireWakeLock(SleepDatum sleepDatum) {
|
||||
WakeLock wakeLock = sleepDatum.wakeLock;
|
||||
if (wakeLock != null) {
|
||||
synchronized (wakeLock) {
|
||||
long timeout = sleepDatum.timeout;
|
||||
Timber.d("SleepService Acquiring wakeLock for %d ms", timeout);
|
||||
wakeLock.acquire(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int startService(Intent intent, int startId) {
|
||||
try {
|
||||
if (intent.getAction().startsWith(ALARM_FIRED)) {
|
||||
Integer id = intent.getIntExtra(LATCH_ID, -1);
|
||||
endSleep(id);
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
finally {
|
||||
stopSelf(startId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SleepDatum {
|
||||
CountDownLatch latch;
|
||||
WakeLock wakeLock;
|
||||
long timeout;
|
||||
CountDownLatch reacquireLatch;
|
||||
}
|
||||
|
||||
}
|
|
@ -278,14 +278,6 @@
|
|||
android:name="com.fsck.k9.notification.NotificationActionService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.service.PushService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.service.SleepService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.service.DatabaseUpgradeService"
|
||||
android:exported="false"/>
|
||||
|
|
|
@ -360,14 +360,6 @@
|
|||
android:name=".notification.NotificationActionService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name=".service.PushService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name=".service.SleepService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name=".service.DatabaseUpgradeService"
|
||||
android:exported="false"/>
|
||||
|
|
|
@ -6,8 +6,6 @@ import com.fsck.k9.mail.Flag
|
|||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mail.MessagingException
|
||||
import com.fsck.k9.mail.Part
|
||||
import com.fsck.k9.mail.PushReceiver
|
||||
import com.fsck.k9.mail.Pusher
|
||||
|
||||
interface Backend {
|
||||
val supportsSeenFlag: Boolean
|
||||
|
@ -88,8 +86,6 @@ interface Backend {
|
|||
@Throws(MessagingException::class)
|
||||
fun uploadMessage(folderServerId: String, message: Message): String?
|
||||
|
||||
fun createPusher(receiver: PushReceiver): Pusher
|
||||
|
||||
@Throws(MessagingException::class)
|
||||
fun checkIncomingServerSettings()
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ interface BackendFolder {
|
|||
fun setMoreMessages(moreMessages: MoreMessages)
|
||||
fun setLastChecked(timestamp: Long)
|
||||
fun setStatus(status: String?)
|
||||
fun getPushState(): String?
|
||||
fun setPushState(pushState: String?)
|
||||
fun isMessagePresent(messageServerId: String): Boolean
|
||||
fun getMessageFlags(messageServerId: String): Set<Flag>
|
||||
fun setMessageFlag(messageServerId: String, flag: Flag, value: Boolean)
|
||||
|
|
|
@ -16,10 +16,7 @@ import com.fsck.k9.mail.Flag;
|
|||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
import com.fsck.k9.mail.power.PowerManager;
|
||||
import com.fsck.k9.mail.store.imap.ImapPusher;
|
||||
import com.fsck.k9.mail.store.imap.ImapStore;
|
||||
import com.fsck.k9.mail.transport.smtp.SmtpTransport;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -215,12 +212,6 @@ public class ImapBackend implements Backend {
|
|||
return commandUploadMessage.uploadMessage(folderServerId, message);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Pusher createPusher(@NotNull PushReceiver receiver) {
|
||||
return new ImapPusher(imapStore, receiver, powerManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkIncomingServerSettings() throws MessagingException {
|
||||
imapStore.checkSettings();
|
||||
|
|
|
@ -336,15 +336,6 @@ class ImapSync {
|
|||
fetchUnsyncedMessages(syncConfig, remoteFolder, unsyncedMessages, smallMessages, largeMessages, progress,
|
||||
todo, fp, listener);
|
||||
|
||||
String updatedPushState = backendFolder.getPushState();
|
||||
for (ImapMessage message : unsyncedMessages) {
|
||||
String newPushState = remoteFolder.getNewPushState(updatedPushState, message);
|
||||
if (newPushState != null) {
|
||||
updatedPushState = newPushState;
|
||||
}
|
||||
}
|
||||
backendFolder.setPushState(updatedPushState);
|
||||
|
||||
Timber.d("SYNC: Synced unsynced messages for folder %s", folder);
|
||||
}
|
||||
|
||||
|
@ -416,10 +407,6 @@ class ImapSync {
|
|||
|
||||
unsyncedMessages.add(message);
|
||||
} else {
|
||||
String newPushState = remoteFolder.getNewPushState(backendFolder.getPushState(), message);
|
||||
if (newPushState != null) {
|
||||
backendFolder.setPushState(newPushState);
|
||||
}
|
||||
syncFlagMessages.add(message);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -9,8 +9,6 @@ import com.fsck.k9.mail.FetchProfile
|
|||
import com.fsck.k9.mail.Flag
|
||||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mail.Part
|
||||
import com.fsck.k9.mail.PushReceiver
|
||||
import com.fsck.k9.mail.Pusher
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import rs.ltt.jmap.client.JmapClient
|
||||
|
@ -121,10 +119,6 @@ class JmapBackend(
|
|||
return commandUpload.uploadMessage(folderServerId, message)
|
||||
}
|
||||
|
||||
override fun createPusher(receiver: PushReceiver): Pusher {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun checkIncomingServerSettings() {
|
||||
jmapClient.call(EchoMethodCall()).get()
|
||||
}
|
||||
|
|
|
@ -91,14 +91,6 @@ class InMemoryBackendFolder(override var name: String, var type: FolderType) : B
|
|||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun getPushState(): String? {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun setPushState(pushState: String?) {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun isMessagePresent(messageServerId: String): Boolean {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ import com.fsck.k9.mail.FetchProfile
|
|||
import com.fsck.k9.mail.Flag
|
||||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mail.Part
|
||||
import com.fsck.k9.mail.PushReceiver
|
||||
import com.fsck.k9.mail.Pusher
|
||||
import com.fsck.k9.mail.store.pop3.Pop3Store
|
||||
import com.fsck.k9.mail.transport.smtp.SmtpTransport
|
||||
|
||||
|
@ -120,10 +118,6 @@ class Pop3Backend(
|
|||
throw UnsupportedOperationException("not supported")
|
||||
}
|
||||
|
||||
override fun createPusher(receiver: PushReceiver): Pusher {
|
||||
throw UnsupportedOperationException("not supported")
|
||||
}
|
||||
|
||||
override fun checkIncomingServerSettings() {
|
||||
pop3Store.checkSettings()
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import com.fsck.k9.mail.Flag
|
|||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mail.MessagingException
|
||||
import com.fsck.k9.mail.Part
|
||||
import com.fsck.k9.mail.PushReceiver
|
||||
import com.fsck.k9.mail.Pusher
|
||||
import com.fsck.k9.mail.store.webdav.WebDavStore
|
||||
import com.fsck.k9.mail.transport.WebDavTransport
|
||||
import timber.log.Timber
|
||||
|
@ -130,10 +128,6 @@ class WebDavBackend(
|
|||
return commandUploadMessage.uploadMessage(folderServerId, message)
|
||||
}
|
||||
|
||||
override fun createPusher(receiver: PushReceiver): Pusher {
|
||||
throw UnsupportedOperationException("not supported")
|
||||
}
|
||||
|
||||
override fun checkIncomingServerSettings() {
|
||||
webDavStore.checkSettings()
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package com.fsck.k9.mail;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fsck.k9.mail.power.WakeLock;
|
||||
|
||||
|
||||
public interface PushReceiver {
|
||||
void syncFolder(String folderServerId);
|
||||
void messagesArrived(String folderServerId, List<Message> mess);
|
||||
void messagesFlagsChanged(String folderServerId, List<Message> mess);
|
||||
void messagesRemoved(String folderServerId, List<Message> mess);
|
||||
String getPushState(String folderServerId);
|
||||
void pushError(String errorMessage, Exception e);
|
||||
void authenticationFailed();
|
||||
void setPushActive(String folderServerId, boolean enabled);
|
||||
void sleep(WakeLock wakeLock, long millis);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package com.fsck.k9.mail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface Pusher {
|
||||
void start(List<String> folderServerIds);
|
||||
void refresh();
|
||||
void stop();
|
||||
/**
|
||||
*
|
||||
* @return milliseconds of required refresh interval
|
||||
*/
|
||||
int getRefreshInterval();
|
||||
void setLastRefresh(long lastRefresh);
|
||||
long getLastRefresh();
|
||||
}
|
|
@ -1261,27 +1261,6 @@ public class ImapFolder {
|
|||
}
|
||||
}
|
||||
|
||||
public String getNewPushState(String oldSerializedPushState, Message message) {
|
||||
try {
|
||||
String uid = message.getUid();
|
||||
long messageUid = Long.parseLong(uid);
|
||||
|
||||
ImapPushState oldPushState = ImapPushState.parse(oldSerializedPushState);
|
||||
|
||||
if (messageUid >= oldPushState.uidNext) {
|
||||
long uidNext = messageUid + 1;
|
||||
ImapPushState newPushState = new ImapPushState(uidNext);
|
||||
|
||||
return newPushState.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Exception while updated push state for %s", getLogId());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlags(List<? extends Message> messages, final Set<Flag> flags, boolean value)
|
||||
throws MessagingException {
|
||||
open(OPEN_MODE_RW);
|
||||
|
|
|
@ -1,728 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||
import com.fsck.k9.mail.CertificateValidationException;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.K9MailLib;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.power.PowerManager;
|
||||
import com.fsck.k9.mail.power.WakeLock;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static com.fsck.k9.mail.K9MailLib.PUSH_WAKE_LOCK_TIMEOUT;
|
||||
import static com.fsck.k9.mail.NetworkTimeouts.SOCKET_READ_TIMEOUT;
|
||||
import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase;
|
||||
|
||||
|
||||
class ImapFolderPusher extends ImapFolder {
|
||||
private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000;
|
||||
private static final int IDLE_FAILURE_COUNT_LIMIT = 10;
|
||||
private static final int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes
|
||||
private static final int NORMAL_DELAY_TIME = 5000;
|
||||
|
||||
|
||||
private final PushReceiver pushReceiver;
|
||||
private final Object threadLock = new Object();
|
||||
private final IdleStopper idleStopper = new IdleStopper();
|
||||
private final WakeLock wakeLock;
|
||||
private final List<ImapResponse> storedUntaggedResponses = new ArrayList<>();
|
||||
private Thread listeningThread;
|
||||
private volatile boolean stop = false;
|
||||
private volatile boolean idling = false;
|
||||
|
||||
|
||||
public ImapFolderPusher(ImapStore store, String serverId, PushReceiver pushReceiver, PowerManager powerManager) {
|
||||
super(store, serverId);
|
||||
this.pushReceiver = pushReceiver;
|
||||
|
||||
String tag = "ImapFolderPusher " + store.getStoreConfig().toString() + ":" + getServerId();
|
||||
wakeLock = powerManager.newWakeLock(tag);
|
||||
wakeLock.setReferenceCounted(false);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
synchronized (threadLock) {
|
||||
if (listeningThread != null) {
|
||||
throw new IllegalStateException("start() called twice");
|
||||
}
|
||||
|
||||
listeningThread = new Thread(new PushRunnable());
|
||||
listeningThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
if (idling) {
|
||||
wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
|
||||
idleStopper.stopIdle();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronized (threadLock) {
|
||||
if (listeningThread == null) {
|
||||
throw new IllegalStateException("stop() called twice");
|
||||
}
|
||||
|
||||
stop = true;
|
||||
|
||||
listeningThread.interrupt();
|
||||
listeningThread = null;
|
||||
}
|
||||
|
||||
ImapConnection conn = connection;
|
||||
if (conn != null) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.v("Closing connection to stop pushing for %s", getLogId());
|
||||
}
|
||||
|
||||
conn.close();
|
||||
} else {
|
||||
Timber.w("Attempt to interrupt null connection to stop pushing on folderPusher for %s", getLogId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUntaggedResponse(ImapResponse response) {
|
||||
if (response.getTag() == null && response.size() > 1) {
|
||||
Object responseType = response.get(1);
|
||||
if (equalsIgnoreCase(responseType, "FETCH") || equalsIgnoreCase(responseType, "EXPUNGE") ||
|
||||
equalsIgnoreCase(responseType, "EXISTS")) {
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Storing response %s for later processing", response);
|
||||
}
|
||||
|
||||
synchronized (storedUntaggedResponses) {
|
||||
storedUntaggedResponses.add(response);
|
||||
}
|
||||
}
|
||||
|
||||
handlePossibleUidNext(response);
|
||||
}
|
||||
}
|
||||
|
||||
private void superHandleUntaggedResponse(ImapResponse response) {
|
||||
super.handleUntaggedResponse(response);
|
||||
}
|
||||
|
||||
|
||||
private class PushRunnable implements Runnable, UntaggedHandler {
|
||||
private int delayTime = NORMAL_DELAY_TIME;
|
||||
private int idleFailureCount = 0;
|
||||
private boolean needsPoll = false;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Pusher starting for %s", getLogId());
|
||||
}
|
||||
|
||||
long lastUidNext = -1L;
|
||||
while (!stop) {
|
||||
try {
|
||||
long oldUidNext = getOldUidNext();
|
||||
|
||||
/*
|
||||
* This makes sure 'oldUidNext' is never smaller than 'UIDNEXT' from
|
||||
* the last loop iteration. This way we avoid looping endlessly causing
|
||||
* the battery to drain.
|
||||
*
|
||||
* See issue 4907
|
||||
*/
|
||||
if (oldUidNext < lastUidNext) {
|
||||
oldUidNext = lastUidNext;
|
||||
}
|
||||
|
||||
boolean openedNewConnection = openConnectionIfNecessary();
|
||||
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
boolean pushPollOnConnect = store.getStoreConfig().isPushPollOnConnect();
|
||||
if (pushPollOnConnect && (openedNewConnection || needsPoll)) {
|
||||
needsPoll = false;
|
||||
syncFolderOnConnect();
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
long newUidNext = getNewUidNext();
|
||||
lastUidNext = newUidNext;
|
||||
long startUid = getStartUid(oldUidNext, newUidNext);
|
||||
|
||||
if (newUidNext > startUid) {
|
||||
notifyMessagesArrived(startUid, newUidNext);
|
||||
} else {
|
||||
processStoredUntaggedResponses();
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("About to IDLE for %s", getLogId());
|
||||
}
|
||||
|
||||
prepareForIdle();
|
||||
|
||||
ImapConnection conn = connection;
|
||||
setReadTimeoutForIdle(conn);
|
||||
sendIdle(conn);
|
||||
|
||||
returnFromIdle();
|
||||
}
|
||||
} catch (AuthenticationFailedException e) {
|
||||
reacquireWakeLockAndCleanUp();
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.e(e, "Authentication failed. Stopping ImapFolderPusher.");
|
||||
}
|
||||
|
||||
pushReceiver.authenticationFailed();
|
||||
stop = true;
|
||||
} catch (CertificateValidationException e) {
|
||||
reacquireWakeLockAndCleanUp();
|
||||
|
||||
Timber.e(e, "Certificate check failed. Stopping ImapFolderPusher.");
|
||||
stop = true;
|
||||
pushReceiver.pushError("Push error for " + getServerId(), e);
|
||||
} catch (Exception e) {
|
||||
reacquireWakeLockAndCleanUp();
|
||||
|
||||
if (stop) {
|
||||
Timber.i("Got exception while idling, but stop is set for %s", getLogId());
|
||||
} else {
|
||||
pushReceiver.pushError("Push error for " + getServerId(), e);
|
||||
Timber.e("Got exception while idling for %s", getLogId());
|
||||
|
||||
pushReceiver.sleep(wakeLock, delayTime);
|
||||
|
||||
delayTime *= 2;
|
||||
if (delayTime > MAX_DELAY_TIME) {
|
||||
delayTime = MAX_DELAY_TIME;
|
||||
}
|
||||
|
||||
idleFailureCount++;
|
||||
if (idleFailureCount > IDLE_FAILURE_COUNT_LIMIT) {
|
||||
Timber.e("Disabling pusher for %s after %d consecutive errors", getLogId(), idleFailureCount);
|
||||
pushReceiver.pushError("Push disabled for " + getServerId() + " after " + idleFailureCount +
|
||||
" consecutive errors", e);
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pushReceiver.setPushActive(getServerId(), false);
|
||||
|
||||
try {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Pusher for %s is exiting", getLogId());
|
||||
}
|
||||
|
||||
close();
|
||||
} catch (Exception me) {
|
||||
Timber.e(me, "Got exception while closing for %s", getLogId());
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void reacquireWakeLockAndCleanUp() {
|
||||
wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
|
||||
|
||||
clearStoredUntaggedResponses();
|
||||
idling = false;
|
||||
pushReceiver.setPushActive(getServerId(), false);
|
||||
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Exception me) {
|
||||
Timber.e(me, "Got exception while closing for exception for %s", getLogId());
|
||||
}
|
||||
|
||||
connection = null;
|
||||
}
|
||||
|
||||
private long getNewUidNext() throws MessagingException {
|
||||
long newUidNext = uidNext;
|
||||
if (newUidNext != -1L) {
|
||||
return newUidNext;
|
||||
}
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("uidNext is -1, using search to find highest UID");
|
||||
}
|
||||
|
||||
long highestUid = getHighestUid();
|
||||
if (highestUid == -1L) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
newUidNext = highestUid + 1;
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("highest UID = %d, set newUidNext to %d", highestUid, newUidNext);
|
||||
}
|
||||
|
||||
return newUidNext;
|
||||
}
|
||||
|
||||
private long getStartUid(long oldUidNext, long newUidNext) {
|
||||
long startUid = oldUidNext;
|
||||
int displayCount = store.getStoreConfig().getDisplayCount();
|
||||
|
||||
if (startUid < newUidNext - displayCount) {
|
||||
startUid = newUidNext - displayCount;
|
||||
}
|
||||
|
||||
if (startUid < 1) {
|
||||
startUid = 1;
|
||||
}
|
||||
|
||||
return startUid;
|
||||
}
|
||||
|
||||
private void prepareForIdle() {
|
||||
pushReceiver.setPushActive(getServerId(), true);
|
||||
idling = true;
|
||||
}
|
||||
|
||||
private void sendIdle(ImapConnection conn) throws MessagingException, IOException {
|
||||
String tag = conn.sendCommand(Commands.IDLE, false);
|
||||
|
||||
List<ImapResponse> responses;
|
||||
try {
|
||||
try {
|
||||
responses = conn.readStatusResponse(tag, Commands.IDLE, this);
|
||||
} finally {
|
||||
idleStopper.stopAcceptingDoneContinuation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
conn.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
handleUntaggedResponses(responses);
|
||||
}
|
||||
|
||||
private void returnFromIdle() {
|
||||
idling = false;
|
||||
delayTime = NORMAL_DELAY_TIME;
|
||||
idleFailureCount = 0;
|
||||
}
|
||||
|
||||
private boolean openConnectionIfNecessary() throws MessagingException {
|
||||
ImapConnection oldConnection = connection;
|
||||
internalOpen(OPEN_MODE_RO);
|
||||
|
||||
ImapConnection conn = connection;
|
||||
|
||||
checkConnectionNotNull(conn);
|
||||
checkConnectionIdleCapable(conn);
|
||||
|
||||
return conn != oldConnection;
|
||||
}
|
||||
|
||||
private void checkConnectionNotNull(ImapConnection conn) throws MessagingException {
|
||||
if (conn == null) {
|
||||
String message = "Could not establish connection for IDLE";
|
||||
pushReceiver.pushError(message, null);
|
||||
|
||||
throw new MessagingException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConnectionIdleCapable(ImapConnection conn) throws MessagingException {
|
||||
if (!conn.isIdleCapable()) {
|
||||
stop = true;
|
||||
|
||||
String message = "IMAP server is not IDLE capable: " + conn.toString();
|
||||
pushReceiver.pushError(message, null);
|
||||
|
||||
throw new MessagingException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void setReadTimeoutForIdle(ImapConnection conn) throws SocketException {
|
||||
int idleRefreshTimeout = store.getStoreConfig().getIdleRefreshMinutes() * 60 * 1000;
|
||||
conn.setReadTimeout(idleRefreshTimeout + IDLE_READ_TIMEOUT_INCREMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAsyncUntaggedResponse(ImapResponse response) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.v("Got async response: %s", response);
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Got async untagged response: %s, but stop is set for %s", response, getLogId());
|
||||
}
|
||||
|
||||
idleStopper.stopIdle();
|
||||
} else {
|
||||
if (response.getTag() == null) {
|
||||
if (response.size() > 1) {
|
||||
Object responseType = response.get(1);
|
||||
if (equalsIgnoreCase(responseType, "EXISTS") || equalsIgnoreCase(responseType, "EXPUNGE") ||
|
||||
equalsIgnoreCase(responseType, "FETCH")) {
|
||||
|
||||
wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Got useful async untagged response: %s for %s", response, getLogId());
|
||||
}
|
||||
|
||||
idleStopper.stopIdle();
|
||||
}
|
||||
} else if (response.isContinuationRequested()) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Idling %s", getLogId());
|
||||
}
|
||||
|
||||
idleStopper.startAcceptingDoneContinuation(connection);
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearStoredUntaggedResponses() {
|
||||
synchronized (storedUntaggedResponses) {
|
||||
storedUntaggedResponses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void processStoredUntaggedResponses() throws MessagingException {
|
||||
while (true) {
|
||||
List<ImapResponse> untaggedResponses = getAndClearStoredUntaggedResponses();
|
||||
if (untaggedResponses.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Processing %d untagged responses from previous commands for %s",
|
||||
untaggedResponses.size(), getLogId());
|
||||
}
|
||||
|
||||
processUntaggedResponses(untaggedResponses);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ImapResponse> getAndClearStoredUntaggedResponses() {
|
||||
synchronized (storedUntaggedResponses) {
|
||||
if (storedUntaggedResponses.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ImapResponse> untaggedResponses = new ArrayList<>(storedUntaggedResponses);
|
||||
storedUntaggedResponses.clear();
|
||||
|
||||
return untaggedResponses;
|
||||
}
|
||||
}
|
||||
|
||||
private void processUntaggedResponses(List<ImapResponse> responses) throws MessagingException {
|
||||
boolean skipSync = false;
|
||||
|
||||
int oldMessageCount = messageCount;
|
||||
if (oldMessageCount == -1) {
|
||||
skipSync = true;
|
||||
}
|
||||
|
||||
List<Long> flagSyncMsgSeqs = new ArrayList<>();
|
||||
List<String> removeMsgUids = new LinkedList<>();
|
||||
|
||||
for (ImapResponse response : responses) {
|
||||
oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs, removeMsgUids);
|
||||
}
|
||||
|
||||
if (!skipSync) {
|
||||
if (oldMessageCount < 0) {
|
||||
oldMessageCount = 0;
|
||||
}
|
||||
|
||||
if (messageCount > oldMessageCount) {
|
||||
syncMessages(messageCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("UIDs for messages needing flag sync are %s for %s", flagSyncMsgSeqs, getLogId());
|
||||
}
|
||||
|
||||
if (!flagSyncMsgSeqs.isEmpty()) {
|
||||
syncMessages(flagSyncMsgSeqs);
|
||||
}
|
||||
|
||||
if (!removeMsgUids.isEmpty()) {
|
||||
removeMessages(removeMsgUids);
|
||||
}
|
||||
}
|
||||
|
||||
private int processUntaggedResponse(long oldMessageCount, ImapResponse response, List<Long> flagSyncMsgSeqs,
|
||||
List<String> removeMsgUids) {
|
||||
superHandleUntaggedResponse(response);
|
||||
|
||||
int messageCountDelta = 0;
|
||||
if (response.getTag() == null && response.size() > 1) {
|
||||
try {
|
||||
Object responseType = response.get(1);
|
||||
if (equalsIgnoreCase(responseType, "FETCH")) {
|
||||
Timber.i("Got FETCH %s", response);
|
||||
|
||||
long msgSeq = response.getLong(0);
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Got untagged FETCH for msgseq %d for %s", msgSeq, getLogId());
|
||||
}
|
||||
|
||||
if (!flagSyncMsgSeqs.contains(msgSeq)) {
|
||||
flagSyncMsgSeqs.add(msgSeq);
|
||||
}
|
||||
}
|
||||
|
||||
if (equalsIgnoreCase(responseType, "EXPUNGE")) {
|
||||
long msgSeq = response.getLong(0);
|
||||
if (msgSeq <= oldMessageCount) {
|
||||
messageCountDelta = -1;
|
||||
}
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Got untagged EXPUNGE for msgseq %d for %s", msgSeq, getLogId());
|
||||
}
|
||||
|
||||
List<Long> newSeqs = new ArrayList<>();
|
||||
Iterator<Long> flagIter = flagSyncMsgSeqs.iterator();
|
||||
while (flagIter.hasNext()) {
|
||||
long flagMsg = flagIter.next();
|
||||
if (flagMsg >= msgSeq) {
|
||||
flagIter.remove();
|
||||
if (flagMsg > msgSeq) {
|
||||
newSeqs.add(flagMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flagSyncMsgSeqs.addAll(newSeqs);
|
||||
|
||||
List<Long> msgSeqs = new ArrayList<>(msgSeqUidMap.keySet());
|
||||
Collections.sort(msgSeqs); // Have to do comparisons in order because of msgSeq reductions
|
||||
|
||||
for (long msgSeqNum : msgSeqs) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.v("Comparing EXPUNGEd msgSeq %d to %d", msgSeq, msgSeqNum);
|
||||
}
|
||||
|
||||
if (msgSeqNum == msgSeq) {
|
||||
String uid = msgSeqUidMap.get(msgSeqNum);
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Scheduling removal of UID %s because msgSeq %d was expunged", uid, msgSeqNum);
|
||||
}
|
||||
|
||||
removeMsgUids.add(uid);
|
||||
msgSeqUidMap.remove(msgSeqNum);
|
||||
} else if (msgSeqNum > msgSeq) {
|
||||
String uid = msgSeqUidMap.get(msgSeqNum);
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.d("Reducing msgSeq for UID %s from %d to %d", uid, msgSeqNum, (msgSeqNum - 1));
|
||||
}
|
||||
|
||||
msgSeqUidMap.remove(msgSeqNum);
|
||||
msgSeqUidMap.put(msgSeqNum - 1, uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Could not handle untagged FETCH for %s", getLogId());
|
||||
}
|
||||
}
|
||||
|
||||
return messageCountDelta;
|
||||
}
|
||||
|
||||
private void syncMessages(int end) throws MessagingException {
|
||||
long oldUidNext = getOldUidNext();
|
||||
|
||||
List<ImapMessage> messageList = getMessages(end, end, null, true, null);
|
||||
|
||||
if (messageList != null && messageList.size() > 0) {
|
||||
long newUid = Long.parseLong(messageList.get(0).getUid());
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Got newUid %s for message %d on %s", newUid, end, getLogId());
|
||||
}
|
||||
|
||||
long startUid = oldUidNext;
|
||||
if (startUid < newUid - 10) {
|
||||
startUid = newUid - 10;
|
||||
}
|
||||
|
||||
if (startUid < 1) {
|
||||
startUid = 1;
|
||||
}
|
||||
|
||||
if (newUid >= startUid) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Needs sync from uid %d to %d for %s", startUid, newUid, getLogId());
|
||||
}
|
||||
|
||||
List<Message> messages = new ArrayList<>();
|
||||
for (long uid = startUid; uid <= newUid; uid++) {
|
||||
ImapMessage message = new ImapMessage(Long.toString(uid));
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
pushReceiver.messagesArrived(getServerId(), messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void syncMessages(List<Long> flagSyncMsgSeqs) {
|
||||
try {
|
||||
Set<Long> messageSeqSet = new HashSet<>(flagSyncMsgSeqs);
|
||||
List<? extends Message> messageList = getMessages(messageSeqSet, true, null);
|
||||
|
||||
List<Message> messages = new ArrayList<>(messageList);
|
||||
pushReceiver.messagesFlagsChanged(getServerId(), messages);
|
||||
} catch (Exception e) {
|
||||
pushReceiver.pushError("Exception while processing Push untagged responses", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeMessages(List<String> removeUids) {
|
||||
List<Message> messages = new ArrayList<>(removeUids.size());
|
||||
|
||||
try {
|
||||
List<ImapMessage> existingMessages = getMessagesFromUids(removeUids);
|
||||
for (Message existingMessage : existingMessages) {
|
||||
needsPoll = true;
|
||||
msgSeqUidMap.clear();
|
||||
|
||||
String existingUid = existingMessage.getUid();
|
||||
Timber.w("Message with UID %s still exists on server, not expunging", existingUid);
|
||||
|
||||
removeUids.remove(existingUid);
|
||||
}
|
||||
|
||||
for (String uid : removeUids) {
|
||||
ImapMessage message = new ImapMessage(uid);
|
||||
|
||||
try {
|
||||
message.setFlag(Flag.DELETED, true);
|
||||
} catch (MessagingException me) {
|
||||
Timber.e("Unable to set DELETED flag on message %s", message.getUid());
|
||||
}
|
||||
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
pushReceiver.messagesRemoved(getServerId(), messages);
|
||||
} catch (Exception e) {
|
||||
Timber.e("Cannot remove EXPUNGEd messages");
|
||||
}
|
||||
}
|
||||
|
||||
private void syncFolderOnConnect() throws MessagingException {
|
||||
processStoredUntaggedResponses();
|
||||
|
||||
if (messageCount == -1) {
|
||||
throw new MessagingException("Message count = -1 for idling");
|
||||
}
|
||||
|
||||
pushReceiver.syncFolder(getServerId());
|
||||
}
|
||||
|
||||
private void notifyMessagesArrived(long startUid, long uidNext) {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Needs sync from uid %d to %d for %s", startUid, uidNext, getLogId());
|
||||
}
|
||||
|
||||
int count = (int) (uidNext - startUid);
|
||||
List<Message> messages = new ArrayList<>(count);
|
||||
|
||||
for (long uid = startUid; uid < uidNext; uid++) {
|
||||
ImapMessage message = new ImapMessage(Long.toString(uid));
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
pushReceiver.messagesArrived(getServerId(), messages);
|
||||
}
|
||||
|
||||
private long getOldUidNext() {
|
||||
long oldUidNext = -1L;
|
||||
try {
|
||||
String serializedPushState = pushReceiver.getPushState(getServerId());
|
||||
ImapPushState pushState = ImapPushState.parse(serializedPushState);
|
||||
oldUidNext = pushState.uidNext;
|
||||
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Got oldUidNext %d for %s", oldUidNext, getLogId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Unable to get oldUidNext for %s", getLogId());
|
||||
}
|
||||
|
||||
return oldUidNext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the DONE continuation is only sent when the IDLE command was sent and hasn't completed yet.
|
||||
*/
|
||||
private static class IdleStopper {
|
||||
private boolean acceptDoneContinuation = false;
|
||||
private ImapConnection imapConnection;
|
||||
|
||||
|
||||
public synchronized void startAcceptingDoneContinuation(ImapConnection connection) {
|
||||
if (connection == null) {
|
||||
throw new NullPointerException("connection must not be null");
|
||||
}
|
||||
|
||||
acceptDoneContinuation = true;
|
||||
imapConnection = connection;
|
||||
}
|
||||
|
||||
public synchronized void stopAcceptingDoneContinuation() {
|
||||
acceptDoneContinuation = false;
|
||||
imapConnection = null;
|
||||
}
|
||||
|
||||
public synchronized void stopIdle() {
|
||||
if (acceptDoneContinuation) {
|
||||
acceptDoneContinuation = false;
|
||||
sendDone();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendDone() {
|
||||
try {
|
||||
imapConnection.setReadTimeout(SOCKET_READ_TIMEOUT);
|
||||
imapConnection.sendContinuation("DONE");
|
||||
} catch (IOException e) {
|
||||
imapConnection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
class ImapPushState {
|
||||
private static final long DEFAULT_UID_NEXT = -1L;
|
||||
private static final String PUSH_STATE_PREFIX = "uidNext=";
|
||||
private static final int PUSH_STATE_PREFIX_LENGTH = 8;
|
||||
|
||||
|
||||
public final long uidNext;
|
||||
|
||||
public static ImapPushState parse(String pushState) {
|
||||
if (pushState == null || !pushState.startsWith(PUSH_STATE_PREFIX)) {
|
||||
return createDefaultImapPushState();
|
||||
}
|
||||
|
||||
String value = pushState.substring(PUSH_STATE_PREFIX_LENGTH);
|
||||
try {
|
||||
long newUidNext = Long.parseLong(value);
|
||||
|
||||
return new ImapPushState(newUidNext);
|
||||
} catch (NumberFormatException e) {
|
||||
Timber.e(e, "Unable to part uidNext value %s", value);
|
||||
}
|
||||
|
||||
return createDefaultImapPushState();
|
||||
}
|
||||
|
||||
static ImapPushState createDefaultImapPushState() {
|
||||
return new ImapPushState(DEFAULT_UID_NEXT);
|
||||
}
|
||||
|
||||
public ImapPushState(long uidNext) {
|
||||
this.uidNext = uidNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "uidNext=" + uidNext;
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fsck.k9.mail.K9MailLib;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
import com.fsck.k9.mail.power.PowerManager;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class ImapPusher implements Pusher {
|
||||
private final ImapStore store;
|
||||
private final PushReceiver pushReceiver;
|
||||
private final PowerManager powerManager;
|
||||
|
||||
private final List<ImapFolderPusher> folderPushers = new ArrayList<>();
|
||||
|
||||
private long lastRefresh = -1;
|
||||
|
||||
|
||||
public ImapPusher(ImapStore store, PushReceiver pushReceiver, PowerManager powerManager) {
|
||||
this.store = store;
|
||||
this.pushReceiver = pushReceiver;
|
||||
this.powerManager = powerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(List<String> folderServerIds) {
|
||||
synchronized (folderPushers) {
|
||||
stop();
|
||||
|
||||
setLastRefresh(currentTimeMillis());
|
||||
|
||||
for (String folderName : folderServerIds) {
|
||||
ImapFolderPusher pusher = createImapFolderPusher(folderName);
|
||||
folderPushers.add(pusher);
|
||||
|
||||
pusher.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
synchronized (folderPushers) {
|
||||
for (ImapFolderPusher folderPusher : folderPushers) {
|
||||
try {
|
||||
folderPusher.refresh();
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Got exception while refreshing for %s", folderPusher.getServerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Requested stop of IMAP pusher");
|
||||
}
|
||||
|
||||
synchronized (folderPushers) {
|
||||
for (ImapFolderPusher folderPusher : folderPushers) {
|
||||
try {
|
||||
if (K9MailLib.isDebug()) {
|
||||
Timber.i("Requesting stop of IMAP folderPusher %s", folderPusher.getServerId());
|
||||
}
|
||||
|
||||
folderPusher.stop();
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Got exception while stopping %s", folderPusher.getServerId());
|
||||
}
|
||||
}
|
||||
|
||||
folderPushers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshInterval() {
|
||||
return (store.getStoreConfig().getIdleRefreshMinutes() * 60 * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastRefresh() {
|
||||
return lastRefresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastRefresh(long lastRefresh) {
|
||||
this.lastRefresh = lastRefresh;
|
||||
}
|
||||
|
||||
ImapFolderPusher createImapFolderPusher(String folderName) {
|
||||
return new ImapFolderPusher(store, folderName, pushReceiver, powerManager);
|
||||
}
|
||||
|
||||
long currentTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
|
@ -966,28 +966,6 @@ public class ImapFolderTest {
|
|||
assertCommandIssued("UID STORE 1:* +FLAGS.SILENT (\\Seen)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewPushState_withNewerUid_shouldReturnNewPushState() throws Exception {
|
||||
ImapFolder folder = createFolder("Folder");
|
||||
prepareImapFolderForOpen(OPEN_MODE_RW);
|
||||
ImapMessage message = createImapMessage("2");
|
||||
|
||||
String newPushState = folder.getNewPushState("uidNext=2", message);
|
||||
|
||||
assertEquals("uidNext=3", newPushState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewPushState_withoutNewerUid_shouldReturnNull() throws Exception {
|
||||
ImapFolder folder = createFolder("Folder");
|
||||
prepareImapFolderForOpen(OPEN_MODE_RW);
|
||||
ImapMessage message = createImapMessage("1");
|
||||
|
||||
String newPushState = folder.getNewPushState("uidNext=2", message);
|
||||
|
||||
assertNull(newPushState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void search_withFullTextSearchEnabled_shouldIssueRespectiveCommand() throws Exception {
|
||||
ImapFolder folder = createFolder("Folder");
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
|
||||
public class ImapPushStateTest {
|
||||
@Test
|
||||
public void parse_withValidArgument() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse("uidNext=42");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(42L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withNullArgument_shouldReturnUidNextOfMinusOne() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse(null);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(-1L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withEmptyArgument_shouldReturnUidNextOfMinusOne() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse("");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(-1L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withInvalidArgument_shouldReturnUidNextOfMinusOne() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse("xyz");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(-1L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withIncompleteArgument_shouldReturnUidNextOfMinusOne() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse("uidNext=");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(-1L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withoutIntegerAsUidNext_shouldReturnUidNextOfMinusOne() throws Exception {
|
||||
ImapPushState result = ImapPushState.parse("uidNext=xyz");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(-1L, result.uidNext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toString_shouldReturnExpectedResult() throws Exception {
|
||||
ImapPushState imapPushState = new ImapPushState(23L);
|
||||
|
||||
String result = imapPushState.toString();
|
||||
|
||||
assertEquals("uidNext=23", result);
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.power.PowerManager;
|
||||
import com.fsck.k9.mail.store.StoreConfig;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
public class ImapPusherTest {
|
||||
private ImapStore imapStore;
|
||||
private TestImapPusher imapPusher;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
imapStore = mock(ImapStore.class);
|
||||
|
||||
PushReceiver pushReceiver = mock(PushReceiver.class);
|
||||
PowerManager powerManager = mock(PowerManager.class);
|
||||
imapPusher = new TestImapPusher(imapStore, pushReceiver, powerManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_shouldSetLastRefreshToCurrentTime() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("INBOX");
|
||||
|
||||
imapPusher.start(folderNames);
|
||||
|
||||
assertEquals(TestImapPusher.CURRENT_TIME_MILLIS, imapPusher.getLastRefresh());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_withSingleFolderName_shouldCreateImapFolderPusherAndCallStartOnIt() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("INBOX");
|
||||
|
||||
imapPusher.start(folderNames);
|
||||
|
||||
List<ImapFolderPusher> imapFolderPushers = imapPusher.getImapFolderPushers();
|
||||
assertEquals(1, imapFolderPushers.size());
|
||||
ImapFolderPusher imapFolderPusher = imapFolderPushers.get(0);
|
||||
verify(imapFolderPusher).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_calledAfterStart_shouldStopFirstImapFolderPusher() throws Exception {
|
||||
imapPusher.start(Collections.singletonList("Drafts"));
|
||||
|
||||
imapPusher.start(Collections.singletonList("INBOX"));
|
||||
|
||||
ImapFolderPusher draftsPusher = imapPusher.getImapFolderPushers().get(0);
|
||||
verify(draftsPusher).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_withTwoFolderNames_shouldCreateTwoImapFolderPushersAndCallStart() throws Exception {
|
||||
List<String> folderNames = Arrays.asList("Important", "Drafts");
|
||||
|
||||
imapPusher.start(folderNames);
|
||||
|
||||
List<ImapFolderPusher> imapFolderPushers = imapPusher.getImapFolderPushers();
|
||||
assertEquals(2, imapFolderPushers.size());
|
||||
ImapFolderPusher imapFolderPusherOne = imapFolderPushers.get(0);
|
||||
ImapFolderPusher imapFolderPusherTwo = imapFolderPushers.get(1);
|
||||
verify(imapFolderPusherOne).start();
|
||||
verify(imapFolderPusherTwo).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stop_withoutStartBeingCalled_shouldNotCreateAnyImapFolderPushers() throws Exception {
|
||||
imapPusher.stop();
|
||||
|
||||
List<ImapFolderPusher> imapFolderPushers = imapPusher.getImapFolderPushers();
|
||||
assertEquals(0, imapFolderPushers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stop_afterStartWithSingleFolderName_shouldStopImapFolderPusher() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("Archive");
|
||||
imapPusher.start(folderNames);
|
||||
|
||||
imapPusher.stop();
|
||||
|
||||
List<ImapFolderPusher> imapFolderPushers = imapPusher.getImapFolderPushers();
|
||||
assertEquals(1, imapFolderPushers.size());
|
||||
ImapFolderPusher imapFolderPusher = imapFolderPushers.get(0);
|
||||
verify(imapFolderPusher).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stop_withImapFolderPusherThrowing_shouldNotThrow() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("Archive");
|
||||
imapPusher.start(folderNames);
|
||||
ImapFolderPusher imapFolderPusher = imapPusher.getImapFolderPushers().get(0);
|
||||
doThrow(RuntimeException.class).when(imapFolderPusher).stop();
|
||||
|
||||
imapPusher.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_shouldCallRefreshOnStartedImapFolderPusher() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("Trash");
|
||||
imapPusher.start(folderNames);
|
||||
|
||||
imapPusher.refresh();
|
||||
|
||||
List<ImapFolderPusher> imapFolderPushers = imapPusher.getImapFolderPushers();
|
||||
assertEquals(1, imapFolderPushers.size());
|
||||
ImapFolderPusher imapFolderPusher = imapFolderPushers.get(0);
|
||||
verify(imapFolderPusher).refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_withImapFolderPusherThrowing_shouldNotThrow() throws Exception {
|
||||
List<String> folderNames = Collections.singletonList("Folder");
|
||||
imapPusher.start(folderNames);
|
||||
ImapFolderPusher imapFolderPusher = imapPusher.getImapFolderPushers().get(0);
|
||||
doThrow(RuntimeException.class).when(imapFolderPusher).refresh();
|
||||
|
||||
imapPusher.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRefreshInterval() throws Exception {
|
||||
StoreConfig storeConfig = mock(StoreConfig.class);
|
||||
when(storeConfig.getIdleRefreshMinutes()).thenReturn(23);
|
||||
when(imapStore.getStoreConfig()).thenReturn(storeConfig);
|
||||
|
||||
int result = imapPusher.getRefreshInterval();
|
||||
|
||||
assertEquals(23 * 60 * 1000, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLastRefresh_shouldBeMinusOneInitially() throws Exception {
|
||||
long result = imapPusher.getLastRefresh();
|
||||
|
||||
assertEquals(-1L, result);
|
||||
}
|
||||
|
||||
|
||||
static class TestImapPusher extends ImapPusher {
|
||||
public static final long CURRENT_TIME_MILLIS = 1454375675162L;
|
||||
|
||||
|
||||
private final List<ImapFolderPusher> imapFolderPushers = new ArrayList<>();
|
||||
|
||||
|
||||
public TestImapPusher(ImapStore store, PushReceiver receiver, PowerManager powerManager) {
|
||||
super(store, receiver, powerManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
ImapFolderPusher createImapFolderPusher(String folderName) {
|
||||
ImapFolderPusher imapFolderPusher = mock(ImapFolderPusher.class);
|
||||
imapFolderPushers.add(imapFolderPusher);
|
||||
return imapFolderPusher;
|
||||
}
|
||||
|
||||
public List<ImapFolderPusher> getImapFolderPushers() {
|
||||
return imapFolderPushers;
|
||||
}
|
||||
|
||||
@Override
|
||||
long currentTimeMillis() {
|
||||
return CURRENT_TIME_MILLIS;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue