email: support for auto-sync multiple IMAP folders
This enables to auto-sync multiple IMAP folders, not only Inbox. Default to Inbox only. This changes relays in the syncloopback attribute to configure the folders to add to sync process. Change-Id: I8973cfd6ddec33446256bc8b48418558e27596b5 Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
This commit is contained in:
parent
8b1818d4a8
commit
598a070c27
|
@ -299,6 +299,10 @@ public class Mailbox extends EmailContent implements EmailContent.MailboxColumns
|
||||||
MailboxColumns.SYNC_INTERVAL + "=1 and " + MailboxColumns.TYPE + "=? and " +
|
MailboxColumns.SYNC_INTERVAL + "=1 and " + MailboxColumns.TYPE + "=? and " +
|
||||||
MailboxColumns.ACCOUNT_KEY + "=?";
|
MailboxColumns.ACCOUNT_KEY + "=?";
|
||||||
|
|
||||||
|
/** Selection for mailboxes that are configured for sync for an account. */
|
||||||
|
private static final String SYNCING_MAILBOXES_FOR_ACCOUNT_SELECTION =
|
||||||
|
MailboxColumns.SYNC_INTERVAL + "=1 and " + MailboxColumns.ACCOUNT_KEY + "=?";
|
||||||
|
|
||||||
// Types of mailboxes. The list is ordered to match a typical UI presentation, e.g.
|
// Types of mailboxes. The list is ordered to match a typical UI presentation, e.g.
|
||||||
// placing the inbox at the top.
|
// placing the inbox at the top.
|
||||||
// Arrays of "special_mailbox_display_names" and "special_mailbox_icons" are depends on
|
// Arrays of "special_mailbox_display_names" and "special_mailbox_icons" are depends on
|
||||||
|
@ -910,6 +914,19 @@ public class Mailbox extends EmailContent implements EmailContent.MailboxColumns
|
||||||
new String[] { Integer.toString(mailboxType), Long.toString(accountId) }, null);
|
new String[] { Integer.toString(mailboxType), Long.toString(accountId) }, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mailbox ids for an account that are configured for sync by the user.
|
||||||
|
* @param cr The {@link ContentResolver}.
|
||||||
|
* @param accountId The id for the account that is syncing.
|
||||||
|
* @return A cursor (with one column, containing ids) with all mailbox ids that match.
|
||||||
|
*/
|
||||||
|
public static Cursor getLoopBackMailboxIdsForSync(final ContentResolver cr,
|
||||||
|
final long accountId) {
|
||||||
|
return cr.query(Mailbox.CONTENT_URI, Mailbox.ID_PROJECTION,
|
||||||
|
SYNCING_MAILBOXES_FOR_ACCOUNT_SELECTION,
|
||||||
|
new String[] {Long.toString(accountId) }, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the account id for a mailbox.
|
* Get the account id for a mailbox.
|
||||||
* @param context The {@link Context}.
|
* @param context The {@link Context}.
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class SyncWindow {
|
||||||
return 365*10;
|
return 365*10;
|
||||||
case SYNC_WINDOW_ACCOUNT:
|
case SYNC_WINDOW_ACCOUNT:
|
||||||
default:
|
default:
|
||||||
return 14;
|
return 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,8 @@ public final class DBHelper {
|
||||||
// Version 126: Decode address lists for To, From, Cc, Bcc and Reply-To columns in Message.
|
// Version 126: Decode address lists for To, From, Cc, Bcc and Reply-To columns in Message.
|
||||||
// Version 127: Force mFlags to contain the correct flags for EAS accounts given a protocol
|
// Version 127: Force mFlags to contain the correct flags for EAS accounts given a protocol
|
||||||
// version above 12.0
|
// version above 12.0
|
||||||
public static final int DATABASE_VERSION = 128;
|
// Version 129: Update all IMAP INBOX mailboxes to force synchronization
|
||||||
|
public static final int DATABASE_VERSION = 129;
|
||||||
|
|
||||||
// Any changes to the database format *must* include update-in-place code.
|
// Any changes to the database format *must* include update-in-place code.
|
||||||
// Original version: 2
|
// Original version: 2
|
||||||
|
@ -1538,6 +1539,28 @@ public final class DBHelper {
|
||||||
LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v127 to v128", e);
|
LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v127 to v128", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This statement changes the syncInterval column to 1 for all IMAP INBOX mailboxes.
|
||||||
|
// It does this by matching mailboxes against all account IDs whose receive auth is
|
||||||
|
// either R.string.protocol_legacy_imap, R.string.protocol_imap or "imap"
|
||||||
|
// It needed in order to mark
|
||||||
|
// We do it here to avoid the minor collisions with aosp main db
|
||||||
|
if (oldVersion <= 129) {
|
||||||
|
db.execSQL("update " + Mailbox.TABLE_NAME + " set "
|
||||||
|
+ MailboxColumns.SYNC_INTERVAL + "= 1 where "
|
||||||
|
+ MailboxColumns.TYPE + "= " + Mailbox.TYPE_INBOX + " and "
|
||||||
|
+ MailboxColumns.ACCOUNT_KEY + " in (select "
|
||||||
|
+ Account.TABLE_NAME + "." + AccountColumns._ID + " from "
|
||||||
|
+ Account.TABLE_NAME + " join " + HostAuth.TABLE_NAME + " where "
|
||||||
|
+ HostAuth.TABLE_NAME + "." + HostAuthColumns._ID + "="
|
||||||
|
+ Account.TABLE_NAME + "." + AccountColumns.HOST_AUTH_KEY_RECV
|
||||||
|
+ " and (" + HostAuth.TABLE_NAME + "."
|
||||||
|
+ HostAuthColumns.PROTOCOL + "='"
|
||||||
|
+ mContext.getString(R.string.protocol_legacy_imap) + "' or "
|
||||||
|
+ HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='"
|
||||||
|
+ mContext.getString(R.string.protocol_imap) + "' or "
|
||||||
|
+ HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='imap'));");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -419,6 +419,11 @@ public abstract class EmailServiceStub extends IEmailService.Stub implements IEm
|
||||||
mailbox.save(mContext);
|
mailbox.save(mContext);
|
||||||
if (type == Mailbox.TYPE_INBOX) {
|
if (type == Mailbox.TYPE_INBOX) {
|
||||||
inboxId = mailbox.mId;
|
inboxId = mailbox.mId;
|
||||||
|
|
||||||
|
// In a clean start we must mark the Inbox mailbox as syncable. This
|
||||||
|
// is required by the new multiple mailboxes sync. Initially Inbox
|
||||||
|
// should start marked
|
||||||
|
mailbox.mSyncInterval = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,6 @@ import java.util.List;
|
||||||
public class ImapService extends Service {
|
public class ImapService extends Service {
|
||||||
// TODO get these from configurations or settings.
|
// TODO get these from configurations or settings.
|
||||||
private static final long QUICK_SYNC_WINDOW_MILLIS = DateUtils.DAY_IN_MILLIS;
|
private static final long QUICK_SYNC_WINDOW_MILLIS = DateUtils.DAY_IN_MILLIS;
|
||||||
private static final long FULL_SYNC_WINDOW_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
|
|
||||||
private static final long FULL_SYNC_INTERVAL_MILLIS = 4 * DateUtils.HOUR_IN_MILLIS;
|
private static final long FULL_SYNC_INTERVAL_MILLIS = 4 * DateUtils.HOUR_IN_MILLIS;
|
||||||
private static final String TAG = "ImapService";
|
private static final String TAG = "ImapService";
|
||||||
|
|
||||||
|
@ -503,38 +502,12 @@ public class ImapService extends Service {
|
||||||
final boolean fullSync = (uiRefresh || loadMore ||
|
final boolean fullSync = (uiRefresh || loadMore ||
|
||||||
timeSinceLastFullSync >= FULL_SYNC_INTERVAL_MILLIS || timeSinceLastFullSync < 0);
|
timeSinceLastFullSync >= FULL_SYNC_INTERVAL_MILLIS || timeSinceLastFullSync < 0);
|
||||||
|
|
||||||
if (account.mSyncLookback == SyncWindow.SYNC_WINDOW_ALL) {
|
if (fullSync) {
|
||||||
// This is really for testing. There is no UI that allows setting the sync window for
|
int syncLookBack = mailbox.mSyncLookback == SyncWindow.SYNC_WINDOW_ACCOUNT
|
||||||
// IMAP, but it can be set by sending a special intent to AccountSetupFinal activity.
|
? account.mSyncLookback
|
||||||
endDate = 0;
|
: mailbox.mSyncLookback;
|
||||||
} else if (fullSync) {
|
endDate = System.currentTimeMillis() -
|
||||||
// Find the oldest message in the local store. We need our time window to include
|
(SyncWindow.toDays(syncLookBack) * DateUtils.DAY_IN_MILLIS);
|
||||||
// all messages that are currently present locally.
|
|
||||||
endDate = System.currentTimeMillis() - FULL_SYNC_WINDOW_MILLIS;
|
|
||||||
Cursor localOldestCursor = null;
|
|
||||||
try {
|
|
||||||
// b/11520812 Ignore message with timestamp = 0 (which includes NULL)
|
|
||||||
localOldestCursor = resolver.query(EmailContent.Message.CONTENT_URI,
|
|
||||||
OldestTimestampInfo.PROJECTION,
|
|
||||||
EmailContent.MessageColumns.ACCOUNT_KEY + "=?" + " AND " +
|
|
||||||
MessageColumns.MAILBOX_KEY + "=? AND " +
|
|
||||||
MessageColumns.TIMESTAMP + "!=0",
|
|
||||||
new String[] {String.valueOf(account.mId), String.valueOf(mailbox.mId)},
|
|
||||||
null);
|
|
||||||
if (localOldestCursor != null && localOldestCursor.moveToFirst()) {
|
|
||||||
long oldestLocalMessageDate = localOldestCursor.getLong(
|
|
||||||
OldestTimestampInfo.COLUMN_OLDEST_TIMESTAMP);
|
|
||||||
if (oldestLocalMessageDate > 0) {
|
|
||||||
endDate = Math.min(endDate, oldestLocalMessageDate);
|
|
||||||
LogUtils.d(
|
|
||||||
Logging.LOG_TAG, "oldest local message " + oldestLocalMessageDate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (localOldestCursor != null) {
|
|
||||||
localOldestCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogUtils.d(Logging.LOG_TAG, "full sync: original window: now - " + endDate);
|
LogUtils.d(Logging.LOG_TAG, "full sync: original window: now - " + endDate);
|
||||||
} else {
|
} else {
|
||||||
// We are doing a frequent, quick sync. This only syncs a small time window, so that
|
// We are doing a frequent, quick sync. This only syncs a small time window, so that
|
||||||
|
|
|
@ -31,6 +31,7 @@ import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
import com.android.email.R;
|
import com.android.email.R;
|
||||||
|
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
|
||||||
import com.android.emailcommon.TempDirectory;
|
import com.android.emailcommon.TempDirectory;
|
||||||
import com.android.emailcommon.mail.MessagingException;
|
import com.android.emailcommon.mail.MessagingException;
|
||||||
import com.android.emailcommon.provider.Account;
|
import com.android.emailcommon.provider.Account;
|
||||||
|
@ -50,6 +51,9 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
private static final String TAG = "PopImapSyncService";
|
private static final String TAG = "PopImapSyncService";
|
||||||
private SyncAdapterImpl mSyncAdapter = null;
|
private SyncAdapterImpl mSyncAdapter = null;
|
||||||
|
|
||||||
|
private static String sPop3Protocol;
|
||||||
|
private static String sLegacyImapProtocol;
|
||||||
|
|
||||||
public PopImapSyncAdapterService() {
|
public PopImapSyncAdapterService() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -82,17 +86,15 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
* @return whether or not this mailbox retrieves its data from the server (as opposed to just
|
* @return whether or not this mailbox retrieves its data from the server (as opposed to just
|
||||||
* a local mailbox that is never synced).
|
* a local mailbox that is never synced).
|
||||||
*/
|
*/
|
||||||
private static boolean loadsFromServer(Context context, Mailbox m, String protocol) {
|
private static boolean loadsFromServer(Context context, Mailbox m, Account acct) {
|
||||||
String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
|
if (isLegacyImapProtocol(context, acct)) {
|
||||||
String pop3Protocol = context.getString(R.string.protocol_pop3);
|
|
||||||
if (legacyImapProtocol.equals(protocol)) {
|
|
||||||
// TODO: actually use a sync flag when creating the mailboxes. Right now we use an
|
// TODO: actually use a sync flag when creating the mailboxes. Right now we use an
|
||||||
// approximation for IMAP.
|
// approximation for IMAP.
|
||||||
return m.mType != Mailbox.TYPE_DRAFTS
|
return m.mType != Mailbox.TYPE_DRAFTS
|
||||||
&& m.mType != Mailbox.TYPE_OUTBOX
|
&& m.mType != Mailbox.TYPE_OUTBOX
|
||||||
&& m.mType != Mailbox.TYPE_SEARCH;
|
&& m.mType != Mailbox.TYPE_SEARCH;
|
||||||
|
|
||||||
} else if (pop3Protocol.equals(protocol)) {
|
} else if (isPop3Protocol(context, acct)) {
|
||||||
return Mailbox.TYPE_INBOX == m.mType;
|
return Mailbox.TYPE_INBOX == m.mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,9 +110,8 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
|
Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
|
||||||
if (account == null) return;
|
if (account == null) return;
|
||||||
ContentResolver resolver = context.getContentResolver();
|
ContentResolver resolver = context.getContentResolver();
|
||||||
String protocol = account.getProtocol(context);
|
|
||||||
if ((mailbox.mType != Mailbox.TYPE_OUTBOX) &&
|
if ((mailbox.mType != Mailbox.TYPE_OUTBOX) &&
|
||||||
!loadsFromServer(context, mailbox, protocol)) {
|
!loadsFromServer(context, mailbox, account)) {
|
||||||
// This is an update to a message in a non-syncing mailbox; delete this from the
|
// This is an update to a message in a non-syncing mailbox; delete this from the
|
||||||
// updates table and return
|
// updates table and return
|
||||||
resolver.delete(Message.UPDATED_CONTENT_URI, MessageColumns.MAILBOX_KEY + "=?",
|
resolver.delete(Message.UPDATED_CONTENT_URI, MessageColumns.MAILBOX_KEY + "=?",
|
||||||
|
@ -129,7 +130,6 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
try {
|
try {
|
||||||
int lastSyncResult;
|
int lastSyncResult;
|
||||||
try {
|
try {
|
||||||
String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
|
|
||||||
if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
|
if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
|
||||||
EmailServiceStub.sendMailImpl(context, account.mId);
|
EmailServiceStub.sendMailImpl(context, account.mId);
|
||||||
} else {
|
} else {
|
||||||
|
@ -138,7 +138,7 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId,
|
EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId,
|
||||||
EmailServiceStatus.IN_PROGRESS, 0, lastSyncResult);
|
EmailServiceStatus.IN_PROGRESS, 0, lastSyncResult);
|
||||||
final int status;
|
final int status;
|
||||||
if (protocol.equals(legacyImapProtocol)) {
|
if (isLegacyImapProtocol(context, account)) {
|
||||||
status = ImapService.synchronizeMailboxSynchronous(context, account,
|
status = ImapService.synchronizeMailboxSynchronous(context, account,
|
||||||
mailbox, deltaMessageCount != 0, uiRefresh);
|
mailbox, deltaMessageCount != 0, uiRefresh);
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,13 +240,20 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
// Get the id for the mailbox we want to sync.
|
// Get the id for the mailbox we want to sync.
|
||||||
long [] mailboxIds = Mailbox.getMailboxIdsFromBundle(extras);
|
long [] mailboxIds = Mailbox.getMailboxIdsFromBundle(extras);
|
||||||
if (mailboxIds == null || mailboxIds.length == 0) {
|
if (mailboxIds == null || mailboxIds.length == 0) {
|
||||||
// No mailbox specified, just sync the inbox.
|
EmailServiceInfo info = EmailServiceUtils.getServiceInfo(
|
||||||
// TODO: IMAP may eventually want to allow multiple auto-sync mailboxes.
|
context, acct.getProtocol(context));
|
||||||
final long inboxId = Mailbox.findMailboxOfType(context, acct.mId,
|
// No mailbox specified, check if the protocol support auto-sync
|
||||||
Mailbox.TYPE_INBOX);
|
// multiple mailboxes. In that case retrieve the mailboxes to sync
|
||||||
if (inboxId != Mailbox.NO_MAILBOX) {
|
// from the account settings. Otherwise just sync the inbox.
|
||||||
mailboxIds = new long[1];
|
if (info.offerLookback) {
|
||||||
mailboxIds[0] = inboxId;
|
mailboxIds = getLoopBackMailboxIdsForSync(context, acct);
|
||||||
|
} else {
|
||||||
|
final long inboxId = Mailbox.findMailboxOfType(context, acct.mId,
|
||||||
|
Mailbox.TYPE_INBOX);
|
||||||
|
if (inboxId != Mailbox.NO_MAILBOX) {
|
||||||
|
mailboxIds = new long[1];
|
||||||
|
mailboxIds[0] = inboxId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,4 +277,37 @@ public class PopImapSyncAdapterService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isLegacyImapProtocol(Context ctx, Account acct) {
|
||||||
|
if (sLegacyImapProtocol == null) {
|
||||||
|
sLegacyImapProtocol = ctx.getString(R.string.protocol_legacy_imap);
|
||||||
|
}
|
||||||
|
return acct.getProtocol(ctx).equals(sLegacyImapProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPop3Protocol(Context ctx, Account acct) {
|
||||||
|
if (sPop3Protocol == null) {
|
||||||
|
sPop3Protocol = ctx.getString(R.string.protocol_pop3);
|
||||||
|
}
|
||||||
|
return acct.getProtocol(ctx).equals(sPop3Protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] getLoopBackMailboxIdsForSync(Context ctx, Account acct) {
|
||||||
|
final Cursor c = Mailbox.getLoopBackMailboxIdsForSync(ctx.getContentResolver(), acct.mId);
|
||||||
|
if (c == null) {
|
||||||
|
// No mailboxes
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long[] mailboxes = new long[c.getCount()];
|
||||||
|
try {
|
||||||
|
int i = 0;
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
mailboxes[i] = c.getLong(Mailbox.CONTENT_ID_COLUMN);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
return mailboxes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,8 @@
|
||||||
email:offerOAuth="true"
|
email:offerOAuth="true"
|
||||||
email:offerLoadMore="true"
|
email:offerLoadMore="true"
|
||||||
email:offerMoveTo="true"
|
email:offerMoveTo="true"
|
||||||
|
email:offerLookback="true"
|
||||||
|
email:defaultLookback="auto"
|
||||||
/>
|
/>
|
||||||
<emailservice
|
<emailservice
|
||||||
email:protocol="@string/protocol_eas"
|
email:protocol="@string/protocol_eas"
|
||||||
|
|
Loading…
Reference in New Issue