k-9/k9mail/src/main/java/com/fsck/k9/service/MailService.java

428 lines
17 KiB
Java
Raw Normal View History

package com.fsck.k9.service;
import java.util.Collection;
import java.util.Date;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.Account.FolderMode;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Pusher;
public class MailService extends CoreService {
private static final String ACTION_CHECK_MAIL = "com.fsck.k9.intent.action.MAIL_SERVICE_WAKEUP";
private static final String ACTION_RESET = "com.fsck.k9.intent.action.MAIL_SERVICE_RESET";
private static final String ACTION_RESCHEDULE_POLL = "com.fsck.k9.intent.action.MAIL_SERVICE_RESCHEDULE_POLL";
private static final String ACTION_CANCEL = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL";
private static final String ACTION_REFRESH_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_REFRESH_PUSHERS";
private static final String ACTION_RESTART_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_RESTART_PUSHERS";
private static final String CONNECTIVITY_CHANGE = "com.fsck.k9.intent.action.MAIL_SERVICE_CONNECTIVITY_CHANGE";
private static final String CANCEL_CONNECTIVITY_NOTICE = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL_CONNECTIVITY_NOTICE";
private static long nextCheck = -1;
Display a message in the titlebar when K-9 Mail is not performing any message synchronization. This state will happen when no network is available, when background ops/background data/auto-sync settings turn off synchronization, or when no account is set for polling or pushing. This revision respects the intention of r3011 that when K-9 Mail is set for pushing only no message is displayed, as that condition is a normal state. However, it provides valuable feedback in other cases that synchronization is off-line, as was done in r1433. Although the case when the user has intentionally set no account for polling or pushing can be considered "normal" and therefore unworthy of a message, it turns out to be a significant burden to detect that situation when the network is offline, since we normally do not even try to figure out the account configurations in that case. Therefore, without making substantial, otherwise unwarranted, changes to MailService.java, "Syncing off" would be displayed sometimes to such a user, and sometimes not. Since it is presumably a rare case, and there is minimal harm to display the "Syncing off" message, the message will be consistently displayed when no account is set for polling or pushing. Note to translators: "Syncing off" is different than "Polling off". The new phrase "Syncing off" means that K-9 Mail is not performing any synchronization with any mail server. The prior non-English phrases for "Polling off" are currently in-place as translations for "Syncing off", as it seems that the meanings are close enough that it would be better to show the obsolete translations that to fallback to the English "Syncing off". However, better translations of "Syncing off" would be welcome.
2011-02-04 03:26:16 -05:00
private static boolean pushingRequested = false;
private static boolean pollingRequested = false;
private static boolean syncBlocked = false;
public static void actionReset(Context context, Integer wakeLockId) {
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESET);
addWakeLockId(context, i, wakeLockId, true);
context.startService(i);
}
public static void actionRestartPushers(Context context, Integer wakeLockId) {
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESTART_PUSHERS);
addWakeLockId(context, i, wakeLockId, true);
context.startService(i);
}
public static void actionReschedulePoll(Context context, Integer wakeLockId) {
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESCHEDULE_POLL);
addWakeLockId(context, i, wakeLockId, true);
context.startService(i);
}
public static void actionCancel(Context context, Integer wakeLockId) {
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_CANCEL);
addWakeLockId(context, i, wakeLockId, false); // CK:Q: why should we not create a wake lock if one is not already existing like for example in actionReschedulePoll?
context.startService(i);
}
public static void connectivityChange(Context context, Integer wakeLockId) {
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.CONNECTIVITY_CHANGE);
addWakeLockId(context, i, wakeLockId, false); // CK:Q: why should we not create a wake lock if one is not already existing like for example in actionReschedulePoll?
context.startService(i);
}
@Override
public void onCreate() {
super.onCreate();
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: onCreate");
}
@Override
public int startService(Intent intent, int startId) {
long startTime = System.currentTimeMillis();
2011-10-28 23:10:13 -04:00
boolean oldIsSyncDisabled = isSyncDisabled();
boolean doBackground = true;
final boolean hasConnectivity = Utility.hasConnectivity(getApplication());
boolean autoSync = ContentResolver.getMasterSyncAutomatically();
K9.BACKGROUND_OPS bOps = K9.getBackgroundOps();
2011-10-28 23:10:13 -04:00
switch (bOps) {
2011-10-28 23:10:13 -04:00
case NEVER:
doBackground = false;
break;
case ALWAYS:
doBackground = true;
break;
case WHEN_CHECKED_AUTO_SYNC:
doBackground = autoSync;
2011-10-28 23:10:13 -04:00
break;
}
Display a message in the titlebar when K-9 Mail is not performing any message synchronization. This state will happen when no network is available, when background ops/background data/auto-sync settings turn off synchronization, or when no account is set for polling or pushing. This revision respects the intention of r3011 that when K-9 Mail is set for pushing only no message is displayed, as that condition is a normal state. However, it provides valuable feedback in other cases that synchronization is off-line, as was done in r1433. Although the case when the user has intentionally set no account for polling or pushing can be considered "normal" and therefore unworthy of a message, it turns out to be a significant burden to detect that situation when the network is offline, since we normally do not even try to figure out the account configurations in that case. Therefore, without making substantial, otherwise unwarranted, changes to MailService.java, "Syncing off" would be displayed sometimes to such a user, and sometimes not. Since it is presumably a rare case, and there is minimal harm to display the "Syncing off" message, the message will be consistently displayed when no account is set for polling or pushing. Note to translators: "Syncing off" is different than "Polling off". The new phrase "Syncing off" means that K-9 Mail is not performing any synchronization with any mail server. The prior non-English phrases for "Polling off" are currently in-place as translations for "Syncing off", as it seems that the meanings are close enough that it would be better to show the obsolete translations that to fallback to the English "Syncing off". However, better translations of "Syncing off" would be welcome.
2011-02-04 03:26:16 -05:00
2011-10-28 23:10:13 -04:00
syncBlocked = !(doBackground && hasConnectivity);
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "MailService.onStart(" + intent + ", " + startId
+ "), hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground);
Display a message in the titlebar when K-9 Mail is not performing any message synchronization. This state will happen when no network is available, when background ops/background data/auto-sync settings turn off synchronization, or when no account is set for polling or pushing. This revision respects the intention of r3011 that when K-9 Mail is set for pushing only no message is displayed, as that condition is a normal state. However, it provides valuable feedback in other cases that synchronization is off-line, as was done in r1433. Although the case when the user has intentionally set no account for polling or pushing can be considered "normal" and therefore unworthy of a message, it turns out to be a significant burden to detect that situation when the network is offline, since we normally do not even try to figure out the account configurations in that case. Therefore, without making substantial, otherwise unwarranted, changes to MailService.java, "Syncing off" would be displayed sometimes to such a user, and sometimes not. Since it is presumably a rare case, and there is minimal harm to display the "Syncing off" message, the message will be consistently displayed when no account is set for polling or pushing. Note to translators: "Syncing off" is different than "Polling off". The new phrase "Syncing off" means that K-9 Mail is not performing any synchronization with any mail server. The prior non-English phrases for "Polling off" are currently in-place as translations for "Syncing off", as it seems that the meanings are close enough that it would be better to show the obsolete translations that to fallback to the English "Syncing off". However, better translations of "Syncing off" would be welcome.
2011-02-04 03:26:16 -05:00
2011-10-28 23:10:13 -04:00
// MessagingController.getInstance(getApplication()).addListener(mListener);
if (ACTION_CHECK_MAIL.equals(intent.getAction())) {
if (K9.DEBUG)
2011-10-28 23:10:13 -04:00
Log.i(K9.LOG_TAG, "***** MailService *****: checking mail");
if (hasConnectivity && doBackground) {
PollService.startService(this);
}
2011-10-28 23:10:13 -04:00
reschedulePollInBackground(hasConnectivity, doBackground, startId, false);
} else if (ACTION_CANCEL.equals(intent.getAction())) {
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: cancel");
cancel();
} else if (ACTION_RESET.equals(intent.getAction())) {
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: reschedule");
rescheduleAllInBackground(hasConnectivity, doBackground, startId);
} else if (ACTION_RESTART_PUSHERS.equals(intent.getAction())) {
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: restarting pushers");
reschedulePushersInBackground(hasConnectivity, doBackground, startId);
} else if (ACTION_RESCHEDULE_POLL.equals(intent.getAction())) {
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: rescheduling poll");
reschedulePollInBackground(hasConnectivity, doBackground, startId, true);
} else if (ACTION_REFRESH_PUSHERS.equals(intent.getAction())) {
refreshPushersInBackground(hasConnectivity, doBackground, startId);
} else if (CONNECTIVITY_CHANGE.equals(intent.getAction())) {
rescheduleAllInBackground(hasConnectivity, doBackground, startId);
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Got connectivity action with hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground);
} else if (CANCEL_CONNECTIVITY_NOTICE.equals(intent.getAction())) {
/* do nothing */
}
2011-10-28 23:10:13 -04:00
if (isSyncDisabled() != oldIsSyncDisabled) {
MessagingController.getInstance(getApplication()).systemStatusChanged();
}
2011-10-28 23:10:13 -04:00
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "MailService.onStart took " + (System.currentTimeMillis() - startTime) + "ms");
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: onDestroy()");
super.onDestroy();
// MessagingController.getInstance(getApplication()).removeListener(mListener);
}
private void cancel() {
Intent i = new Intent(this, MailService.class);
i.setAction(ACTION_CHECK_MAIL);
BootReceiver.cancelIntent(this, i);
}
private final static String PREVIOUS_INTERVAL = "MailService.previousInterval";
private final static String LAST_CHECK_END = "MailService.lastCheckEnd";
public static void saveLastCheckEnd(Context context) {
long lastCheckEnd = System.currentTimeMillis();
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Saving lastCheckEnd = " + new Date(lastCheckEnd));
Preferences prefs = Preferences.getPreferences(context);
SharedPreferences sPrefs = prefs.getPreferences();
SharedPreferences.Editor editor = sPrefs.edit();
editor.putLong(LAST_CHECK_END, lastCheckEnd);
editor.commit();
}
private void rescheduleAllInBackground(final boolean hasConnectivity,
final boolean doBackground, Integer startId) {
execute(getApplication(), new Runnable() {
@Override
public void run() {
reschedulePoll(hasConnectivity, doBackground, true);
reschedulePushers(hasConnectivity, doBackground);
}
}, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void reschedulePollInBackground(final boolean hasConnectivity,
final boolean doBackground, Integer startId, final boolean considerLastCheckEnd) {
execute(getApplication(), new Runnable() {
public void run() {
reschedulePoll(hasConnectivity, doBackground, considerLastCheckEnd);
}
}, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void reschedulePushersInBackground(final boolean hasConnectivity,
final boolean doBackground, Integer startId) {
execute(getApplication(), new Runnable() {
public void run() {
reschedulePushers(hasConnectivity, doBackground);
}
}, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void refreshPushersInBackground(boolean hasConnectivity, boolean doBackground,
Integer startId) {
if (hasConnectivity && doBackground) {
execute(getApplication(), new Runnable() {
public void run() {
refreshPushers();
schedulePushers();
}
}, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
}
private void reschedulePoll(final boolean hasConnectivity, final boolean doBackground,
boolean considerLastCheckEnd) {
if (!(hasConnectivity && doBackground)) {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "No connectivity, canceling check for " +
getApplication().getPackageName());
}
nextCheck = -1;
cancel();
return;
}
Preferences prefs = Preferences.getPreferences(MailService.this);
SharedPreferences sPrefs = prefs.getPreferences();
int previousInterval = sPrefs.getInt(PREVIOUS_INTERVAL, -1);
long lastCheckEnd = sPrefs.getLong(LAST_CHECK_END, -1);
if (lastCheckEnd > System.currentTimeMillis()) {
Log.i(K9.LOG_TAG, "The database claims that the last time mail was checked was in " +
"the future (" + lastCheckEnd + "). To try to get things back to normal, " +
"the last check time has been reset to: " + System.currentTimeMillis());
lastCheckEnd = System.currentTimeMillis();
}
int shortestInterval = -1;
for (Account account : prefs.getAvailableAccounts()) {
if (account.getAutomaticCheckIntervalMinutes() != -1 &&
account.getFolderSyncMode() != FolderMode.NONE &&
(account.getAutomaticCheckIntervalMinutes() < shortestInterval ||
shortestInterval == -1)) {
shortestInterval = account.getAutomaticCheckIntervalMinutes();
}
}
SharedPreferences.Editor editor = sPrefs.edit();
editor.putInt(PREVIOUS_INTERVAL, shortestInterval);
editor.commit();
if (shortestInterval == -1) {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "No next check scheduled for package " +
getApplication().getPackageName());
}
nextCheck = -1;
pollingRequested = false;
cancel();
} else {
long delay = (shortestInterval * (60 * 1000));
long base = (previousInterval == -1 || lastCheckEnd == -1 ||
!considerLastCheckEnd ? System.currentTimeMillis() : lastCheckEnd);
long nextTime = base + delay;
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "previousInterval = " + previousInterval +
", shortestInterval = " + shortestInterval +
", lastCheckEnd = " + new Date(lastCheckEnd) +
", considerLastCheckEnd = " + considerLastCheckEnd);
}
nextCheck = nextTime;
pollingRequested = true;
try {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "Next check for package " +
getApplication().getPackageName() + " scheduled for " +
new Date(nextTime));
}
} catch (Exception e) {
// I once got a NullPointerException deep in new Date();
Log.e(K9.LOG_TAG, "Exception while logging", e);
}
Intent i = new Intent(this, MailService.class);
i.setAction(ACTION_CHECK_MAIL);
BootReceiver.scheduleIntent(MailService.this, nextTime, i);
}
}
public static boolean isSyncDisabled() {
Display a message in the titlebar when K-9 Mail is not performing any message synchronization. This state will happen when no network is available, when background ops/background data/auto-sync settings turn off synchronization, or when no account is set for polling or pushing. This revision respects the intention of r3011 that when K-9 Mail is set for pushing only no message is displayed, as that condition is a normal state. However, it provides valuable feedback in other cases that synchronization is off-line, as was done in r1433. Although the case when the user has intentionally set no account for polling or pushing can be considered "normal" and therefore unworthy of a message, it turns out to be a significant burden to detect that situation when the network is offline, since we normally do not even try to figure out the account configurations in that case. Therefore, without making substantial, otherwise unwarranted, changes to MailService.java, "Syncing off" would be displayed sometimes to such a user, and sometimes not. Since it is presumably a rare case, and there is minimal harm to display the "Syncing off" message, the message will be consistently displayed when no account is set for polling or pushing. Note to translators: "Syncing off" is different than "Polling off". The new phrase "Syncing off" means that K-9 Mail is not performing any synchronization with any mail server. The prior non-English phrases for "Polling off" are currently in-place as translations for "Syncing off", as it seems that the meanings are close enough that it would be better to show the obsolete translations that to fallback to the English "Syncing off". However, better translations of "Syncing off" would be welcome.
2011-02-04 03:26:16 -05:00
return syncBlocked || (!pollingRequested && !pushingRequested);
}
private void stopPushers() {
MessagingController.getInstance(getApplication()).stopAllPushing();
PushService.stopService(MailService.this);
}
private void reschedulePushers(boolean hasConnectivity, boolean doBackground) {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "Rescheduling pushers");
}
stopPushers();
if (!(hasConnectivity && doBackground)) {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "Not scheduling pushers: connectivity? " + hasConnectivity +
" -- doBackground? " + doBackground);
}
return;
}
setupPushers();
schedulePushers();
}
private void setupPushers() {
boolean pushing = false;
for (Account account : Preferences.getPreferences(MailService.this).getAccounts()) {
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Setting up pushers for account " + account.getDescription());
if (account.isEnabled() && account.isAvailable(getApplicationContext())) {
pushing |= MessagingController.getInstance(getApplication()).setupPushing(account);
} else {
//TODO: setupPushing of unavailable accounts when they become available (sd-card inserted)
}
}
if (pushing) {
PushService.startService(MailService.this);
}
pushingRequested = pushing;
}
private void refreshPushers() {
try {
long nowTime = System.currentTimeMillis();
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Refreshing pushers");
Collection<Pusher> pushers = MessagingController.getInstance(getApplication()).getPushers();
for (Pusher pusher : pushers) {
long lastRefresh = pusher.getLastRefresh();
int refreshInterval = pusher.getRefreshInterval();
long sinceLast = nowTime - lastRefresh;
if (sinceLast + 10000 > refreshInterval) { // Add 10 seconds to keep pushers in sync, avoid drift
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "PUSHREFRESH: refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval
+ ", nowTime = " + nowTime + ", sinceLast = " + sinceLast);
}
pusher.refresh();
pusher.setLastRefresh(nowTime);
} else {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "PUSHREFRESH: NOT refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval
+ ", nowTime = " + nowTime + ", sinceLast = " + sinceLast);
}
}
}
// Whenever we refresh our pushers, send any unsent messages
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "PUSHREFRESH: trying to send mail in all folders!");
}
MessagingController.getInstance(getApplication()).sendPendingMessages(null);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Exception while refreshing pushers", e);
}
}
private void schedulePushers() {
int minInterval = -1;
Collection<Pusher> pushers = MessagingController.getInstance(getApplication()).getPushers();
for (Pusher pusher : pushers) {
int interval = pusher.getRefreshInterval();
if (interval > 0 && (interval < minInterval || minInterval == -1)) {
minInterval = interval;
}
}
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Pusher refresh interval = " + minInterval);
}
if (minInterval > 0) {
long nextTime = System.currentTimeMillis() + minInterval;
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Next pusher refresh scheduled for " + new Date(nextTime));
Intent i = new Intent(this, MailService.class);
i.setAction(ACTION_REFRESH_PUSHERS);
BootReceiver.scheduleIntent(MailService.this, nextTime, i);
}
}
@Override
public IBinder onBind(Intent intent) {
// Unused
return null;
}
public static long getNextPollTime() {
return nextCheck;
}
}