From 200c6bd9fa19b78acc2c1664f858521aa9885353 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Wed, 20 Apr 2011 16:22:41 -0700 Subject: [PATCH] Simplify mailbox synchronization logic The logic is simplified by creating database rows for new mailboxes down in Imapstore. This means that the difference between local and remote folder lists are mailboxes that need to be deleted. Note -- this is still not the final CL. We probably update the database too frequently and the column values aren't updated to support nested folders. Change-Id: Ifbe4e0cf74ba81e5b6156b452ab72c56c35235ab --- .../emailcommon/provider/EmailContent.java | 38 +++++ .../android/email/MessagingController.java | 152 +++++++----------- src/com/android/email/mail/Store.java | 68 +++++++- .../email/mail/store/ExchangeStore.java | 2 +- .../android/email/mail/store/ImapStore.java | 13 +- .../android/email/mail/store/Pop3Store.java | 14 +- .../email/mail/store/ImapStoreUnitTests.java | 4 +- .../email/mail/store/Pop3StoreUnitTests.java | 8 +- .../email/provider/ProviderTestUtils.java | 7 +- .../android/email/provider/ProviderTests.java | 51 ++++-- 10 files changed, 230 insertions(+), 127 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index e0503adbc..f3bb793f7 100644 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -16,6 +16,7 @@ package com.android.emailcommon.provider; +import com.android.emailcommon.Logging; import com.android.emailcommon.utility.TextUtilities; import com.android.emailcommon.utility.Utility; @@ -33,6 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.text.TextUtils; +import android.util.Log; import java.io.File; import java.net.URI; @@ -2250,6 +2252,10 @@ public abstract class EmailContent { MailboxColumns.TYPE + " =?"; private static final String MAILBOX_TYPE_SELECTION = MailboxColumns.TYPE + " =?"; + /** Selection by display name for a given account */ + private static final String NAME_AND_ACCOUNT_SELECTION = + MailboxColumns.DISPLAY_NAME + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?"; + private static final String[] MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION = new String [] { "sum(" + MailboxColumns.UNREAD_COUNT + ")" }; @@ -2387,6 +2393,38 @@ public abstract class EmailContent { } } + /** + * Returns a Mailbox from the database, given its pathname and account id. All mailbox + * paths for a particular account must be unique. + * @param context + * @param accountId the ID of the account + * @param path the fully qualified, remote pathname + */ + public static Mailbox restoreMailboxForPath(Context context, long accountId, String path) { + Cursor c = context.getContentResolver().query( + Mailbox.CONTENT_URI, + Mailbox.CONTENT_PROJECTION, + Mailbox.NAME_AND_ACCOUNT_SELECTION, + new String[] { path, Long.toString(accountId) }, + null); +// TODO for mblank; uncomment when you submit CL Iab059f9a68eecd797914a6229f1ff9c03d0f0800 +// if (c == null) throw new ProviderUnavailableException(); + try { + Mailbox mailbox = null; + if (c.moveToFirst()) { + mailbox = getContent(c, Mailbox.class); + if (c.moveToNext()) { + Log.w(Logging.LOG_TAG, "Multiple mailboxes named \"" + path + "\""); + } + } else { + Log.i(Logging.LOG_TAG, "Could not find mailbox at \"" + path + "\""); + } + return mailbox; + } finally { + c.close(); + } + } + @Override public void restore(Cursor cursor) { mBaseUri = CONTENT_URI; diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java index 5bd0c7542..a52e1a681 100644 --- a/src/com/android/email/MessagingController.java +++ b/src/com/android/email/MessagingController.java @@ -36,6 +36,7 @@ import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.Part; import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.provider.EmailContent.AttachmentColumns; import com.android.emailcommon.provider.EmailContent.Mailbox; @@ -197,38 +198,23 @@ public class MessagingController implements Runnable { return mListeners.isActiveListener(listener); } - /** - * Lightweight class for capturing local mailboxes in an account. Just the columns - * necessary for a sync. - */ - private static class LocalMailboxInfo { - private static final int COLUMN_ID = 0; - private static final int COLUMN_DISPLAY_NAME = 1; - private static final int COLUMN_ACCOUNT_KEY = 2; - private static final int COLUMN_TYPE = 3; + private static final int MAILBOX_COLUMN_ID = 0; + private static final int MAILBOX_COLUMN_DISPLAY_NAME = 1; + private static final int MAILBOX_COLUMN_TYPE = 2; - private static final String[] PROJECTION = new String[] { - EmailContent.RECORD_ID, - MailboxColumns.DISPLAY_NAME, MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE, - }; - - final long mId; - final String mDisplayName; - final long mAccountKey; - final int mType; - - public LocalMailboxInfo(Cursor c) { - mId = c.getLong(COLUMN_ID); - mDisplayName = c.getString(COLUMN_DISPLAY_NAME); - mAccountKey = c.getLong(COLUMN_ACCOUNT_KEY); - mType = c.getInt(COLUMN_TYPE); - } - } + /** Small projection for just the columns required for a sync. */ + private static final String[] MAILBOX_PROJECTION = new String[] { + MailboxColumns.ID, + MailboxColumns.DISPLAY_NAME, + MailboxColumns.TYPE, + }; /** - * Asynchronously synchronize the folder list. If the specified {@link MessagingListener} - * is not {@code null}, it must have been previously added to the set of listeners using the - * {@link #addListener(MessagingListener)}. Otherwise, no actions will be performed. + * Synchronize the folder list with the remote server. Synchronization occurs in the + * background and results are passed through the {@link MessagingListener}. If the + * given listener is not {@code null}, it must have been previously added to the set + * of listeners using the {@link #addListener(MessagingListener)}. Otherwise, no + * actions will be performed. * * TODO this needs to cache the remote folder list * TODO break out an inner listFoldersSynchronized which could simplify checkMail @@ -237,101 +223,71 @@ public class MessagingController implements Runnable { * @param listener A listener to notify */ void listFolders(final long accountId, MessagingListener listener) { - final EmailContent.Account account = - EmailContent.Account.restoreAccountWithId(mContext, accountId); + final Account account = Account.restoreAccountWithId(mContext, accountId); if (account == null) { + Log.i(Logging.LOG_TAG, "Could not load account id " + accountId + + ". Has it been removed?"); return; } mListeners.listFoldersStarted(accountId); put("listFolders", listener, new Runnable() { + // TODO For now, mailbox addition occurs in the server-dependent store implementation, + // but, mailbox removal occurs here. Instead, each store should be responsible for + // content synchronization (addition AND removal) since each store will likely need + // to implement it's own, unique synchronization methodology. public void run() { Cursor localFolderCursor = null; try { - // Step 1: Get remote folders, make a list, and add any local folders - // that don't already exist. - + // Step 1: Get remote mailboxes Store store = Store.getInstance(account, mContext, null); - - Folder[] remoteFolders = store.getAllFolders(); - + Folder[] remoteFolders = store.updateFolders(); HashSet remoteFolderNames = new HashSet(); for (int i = 0, count = remoteFolders.length; i < count; i++) { remoteFolderNames.add(remoteFolders[i].getName()); } - HashMap localFolders = - new HashMap(); - HashSet localFolderNames = new HashSet(); + // Step 2: Get local mailboxes localFolderCursor = mContext.getContentResolver().query( EmailContent.Mailbox.CONTENT_URI, - LocalMailboxInfo.PROJECTION, + MAILBOX_PROJECTION, EmailContent.MailboxColumns.ACCOUNT_KEY + "=?", new String[] { String.valueOf(account.mId) }, null); + + // Step 3: Remove any local mailbox not on the remote list while (localFolderCursor.moveToNext()) { - LocalMailboxInfo info = new LocalMailboxInfo(localFolderCursor); - localFolders.put(info.mDisplayName, info); - localFolderNames.add(info.mDisplayName); - } - - // Short circuit the rest if the sets are the same (the usual case) - if (!remoteFolderNames.equals(localFolderNames)) { - - // They are different, so we have to do some adds and drops - - // Drops first, to make things smaller rather than larger - HashSet localsToDrop = new HashSet(localFolderNames); - localsToDrop.removeAll(remoteFolderNames); - for (String localNameToDrop : localsToDrop) { - LocalMailboxInfo localInfo = localFolders.get(localNameToDrop); - // Exclusion list - never delete local special folders, irrespective - // of server-side existence. - switch (localInfo.mType) { - case Mailbox.TYPE_INBOX: - case Mailbox.TYPE_DRAFTS: - case Mailbox.TYPE_OUTBOX: - case Mailbox.TYPE_SENT: - case Mailbox.TYPE_TRASH: - break; - default: - // Drop all attachment files related to this mailbox - AttachmentUtilities.deleteAllMailboxAttachmentFiles( - mContext, accountId, localInfo.mId); - // Delete the mailbox. Triggers will take care of - // related Message, Body and Attachment records. - Uri uri = ContentUris.withAppendedId( - EmailContent.Mailbox.CONTENT_URI, localInfo.mId); - mContext.getContentResolver().delete(uri, null, null); - break; - } + String mailboxPath + = localFolderCursor.getString(MAILBOX_COLUMN_DISPLAY_NAME); + // Short circuit if we have a remote mailbox with the same name + if (remoteFolderNames.contains(mailboxPath)) { + continue; } - // Now do the adds - remoteFolderNames.removeAll(localFolderNames); - for (String remoteNameToAdd : remoteFolderNames) { - EmailContent.Mailbox box = new EmailContent.Mailbox(); - box.mDisplayName = remoteNameToAdd; - // box.mServerId; - // box.mParentServerId; - // box.mParentKey; - box.mAccountKey = account.mId; - box.mType = LegacyConversions.inferMailboxTypeFromName( - mContext, remoteNameToAdd); - // box.mDelimiter; - // box.mSyncKey; - // box.mSyncLookback; - // box.mSyncFrequency; - // box.mSyncTime; - // box.mUnreadCount; - box.mFlagVisible = true; - // box.mFlags; - box.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT; - box.save(mContext); + int mailboxType = localFolderCursor.getInt(MAILBOX_COLUMN_TYPE); + long mailboxId = localFolderCursor.getLong(MAILBOX_COLUMN_ID); + switch (mailboxType) { + case Mailbox.TYPE_INBOX: + case Mailbox.TYPE_DRAFTS: + case Mailbox.TYPE_OUTBOX: + case Mailbox.TYPE_SENT: + case Mailbox.TYPE_TRASH: + // Never, ever delete special mailboxes + break; + default: + // Drop all attachment files related to this mailbox + AttachmentUtilities.deleteAllMailboxAttachmentFiles( + mContext, accountId, mailboxId); + // Delete the mailbox; database triggers take care of related + // Message, Body and Attachment records + Uri uri = ContentUris.withAppendedId( + Mailbox.CONTENT_URI, mailboxId); + mContext.getContentResolver().delete(uri, null, null); + break; } } mListeners.listFoldersFinished(accountId); } catch (Exception e) { - mListeners.listFoldersFailed(accountId, ""); + mListeners.listFoldersFailed(accountId, e.toString()); } finally { if (localFolderCursor != null) { localFolderCursor.close(); diff --git a/src/com/android/email/mail/Store.java b/src/com/android/email/mail/Store.java index 9e2829a69..c06a7c04c 100644 --- a/src/com/android/email/mail/Store.java +++ b/src/com/android/email/mail/Store.java @@ -17,13 +17,14 @@ package com.android.email.mail; import com.android.email.Email; +import com.android.email.LegacyConversions; import com.android.email.R; import com.android.emailcommon.Logging; import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.HostAuth; -import com.android.emailcommon.utility.Utility; +import com.android.emailcommon.provider.EmailContent.Mailbox; import com.google.common.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParserException; @@ -35,6 +36,7 @@ import android.text.TextUtils; import android.util.Log; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; /** @@ -274,7 +276,14 @@ public abstract class Store { public abstract Folder getFolder(String name) throws MessagingException; - public abstract Folder[] getAllFolders() throws MessagingException; + /** + * Updates the local list of mailboxes according to what is located on the remote server. + * Note: This does not perform folder synchronization and it will not remove mailboxes + * that are stored locally but not remotely. + * @return The set of remote folders + * @throws MessagingException If there was a problem connecting to the remote server + */ + public abstract Folder[] updateFolders() throws MessagingException; public abstract Bundle checkSettings() throws MessagingException; @@ -328,4 +337,59 @@ public abstract class Store { throws MessagingException { return null; } + + /** + * Returns a {@link Mailbox} for the given path. If the path is not in the database, a new + * mailbox will be created. + */ + private static Mailbox getMailboxForPath(Context context, long accountId, String path) { + Mailbox mailbox = Mailbox.restoreMailboxForPath(context, accountId, path); + if (mailbox == null) { + mailbox = new Mailbox(); + } + return mailbox; + } + + /** + * Adds the mailbox with the given path to the folder list and to the database. If the folder + * already exists on the server (e.g. the path is identical), the database row will be updated. + * Otherwise, a new database row will be inserted. + * @param folders the list of folders + * @param mailboxPath The path of the mailbox to add + * @param delimiter A path delimiter. May be {@code null} if there is no delimiter. + */ + protected void addMailbox(Context context, long accountId, String mailboxPath, String delimiter, + ArrayList folders) throws MessagingException { + char delimiterChar = 0; + if (!TextUtils.isEmpty(delimiter)) { + delimiterChar = delimiter.charAt(0); + } + folders.add(getFolder(mailboxPath)); + Mailbox mailbox = getMailboxForPath(context, accountId, mailboxPath); + mailbox.mAccountKey = accountId; + mailbox.mDelimiter = delimiterChar; + mailbox.mDisplayName = mailboxPath; + //mailbox.mFlags; + mailbox.mFlagVisible = true; + //mailbox.mParentKey; + //mailbox.mParentServerId; + mailbox.mServerId = mailboxPath; + //mailbox.mServerId; + //mailbox.mSyncFrequency; + //mailbox.mSyncKey; + //mailbox.mSyncLookback; + //mailbox.mSyncTime; + mailbox.mType = LegacyConversions.inferMailboxTypeFromName(context, mailboxPath); + //box.mUnreadCount; + mailbox.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT; + + // TODO This is horribly inefficient. Only update db if the mailbox has really changed + if (mailbox.isSaved()) { + mailbox.update(context, mailbox.toContentValues()); + } else { + mailbox.save(context); + } + // TODO ?? Add mailbox to Folder object ?? + } + } diff --git a/src/com/android/email/mail/store/ExchangeStore.java b/src/com/android/email/mail/store/ExchangeStore.java index f99469f01..55148ded2 100644 --- a/src/com/android/email/mail/store/ExchangeStore.java +++ b/src/com/android/email/mail/store/ExchangeStore.java @@ -80,7 +80,7 @@ public class ExchangeStore extends Store { } @Override - public Folder[] getAllFolders() { + public Folder[] updateFolders() { return null; } diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java index f47c405ad..0c2c68423 100644 --- a/src/com/android/email/mail/store/ImapStore.java +++ b/src/com/android/email/mail/store/ImapStore.java @@ -93,6 +93,7 @@ public class ImapStore extends Store { static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.FLAGGED }; private final Context mContext; + private final Account mAccount; private Transport mRootTransport; private String mUsername; private String mPassword; @@ -140,6 +141,7 @@ public class ImapStore extends Store { */ private ImapStore(Context context, Account account) throws MessagingException { mContext = context; + mAccount = account; HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); if (recvAuth == null || !STORE_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) { @@ -359,7 +361,7 @@ public class ImapStore extends Store { } @Override - public Folder[] getAllFolders() throws MessagingException { + public Folder[] updateFolders() throws MessagingException { ImapConnection connection = getConnection(); try { ArrayList folders = new ArrayList(); @@ -379,8 +381,8 @@ public class ImapStore extends Store { // Get folder name. ImapString encodedFolder = response.getStringOrEmpty(3); if (encodedFolder.isEmpty()) continue; - String folder = decodeFolderName(encodedFolder.getString(), mPathPrefix); - if (ImapConstants.INBOX.equalsIgnoreCase(folder)) { + String folderName = decodeFolderName(encodedFolder.getString(), mPathPrefix); + if (ImapConstants.INBOX.equalsIgnoreCase(folderName)) { continue; } @@ -389,11 +391,12 @@ public class ImapStore extends Store { includeFolder = false; } if (includeFolder) { - folders.add(getFolder(folder)); + String delimiter = response.getStringOrEmpty(2).toString(); + addMailbox(mContext, mAccount.mId, folderName, delimiter, folders); } } } - folders.add(getFolder(ImapConstants.INBOX)); + addMailbox(mContext, mAccount.mId, ImapConstants.INBOX, null, folders); return folders.toArray(new Folder[] {}); } catch (IOException ioe) { connection.close(); diff --git a/src/com/android/email/mail/store/Pop3Store.java b/src/com/android/email/mail/store/Pop3Store.java index 589d1a495..40d8c5eba 100644 --- a/src/com/android/email/mail/store/Pop3Store.java +++ b/src/com/android/email/mail/store/Pop3Store.java @@ -19,6 +19,7 @@ package com.android.email.mail.store; import com.android.email.Email; import com.android.email.mail.Store; import com.android.email.mail.Transport; +import com.android.email.mail.store.imap.ImapConstants; import com.android.email.mail.transport.MailTransport; import com.android.emailcommon.Logging; import com.android.emailcommon.internet.MimeMessage; @@ -53,6 +54,8 @@ public class Pop3Store extends Store { private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED }; + private final Context mContext; + private final Account mAccount; private Transport mTransport; private String mUsername; private String mPassword; @@ -95,6 +98,9 @@ public class Pop3Store extends Store { * Creates a new store for the given account. */ private Pop3Store(Context context, Account account) throws MessagingException { + mContext = context; + mAccount = account; + HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); if (recvAuth == null || !STORE_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) { throw new MessagingException("Unsupported protocol"); @@ -148,10 +154,10 @@ public class Pop3Store extends Store { } @Override - public Folder[] getAllFolders() { - return new Folder[] { - getFolder("INBOX"), - }; + public Folder[] updateFolders() throws MessagingException { + ArrayList folders = new ArrayList(); + addMailbox(mContext, mAccount.mId, "INBOX", null, folders); + return folders.toArray(new Folder[] {}); } /** diff --git a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java index b291eeaf3..f3afbdc58 100644 --- a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java +++ b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java @@ -1212,7 +1212,7 @@ public class ImapStoreUnitTests extends InstrumentationTestCase { "* lIST (\\HAsNoChildren) \"/\" \"&ZeVnLIqe-\"", // Japanese folder name getNextTag(true) + " oK SUCCESS" }); - Folder[] folders = mStore.getAllFolders(); + Folder[] folders = mStore.updateFolders(); ArrayList list = new ArrayList(); for (Folder f : folders) { @@ -2087,7 +2087,7 @@ public class ImapStoreUnitTests extends InstrumentationTestCase { "* LIST () \"/\" \"" + FOLDER_2 + "\"", getNextTag(true) + " OK SUCCESS" }); - final Folder[] folders = mStore.getAllFolders(); + final Folder[] folders = mStore.updateFolders(); ArrayList list = new ArrayList(); for (Folder f : folders) { diff --git a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java index e6276fb1a..9461c396f 100644 --- a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java +++ b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java @@ -246,10 +246,10 @@ public class Pop3StoreUnitTests extends InstrumentationTestCase { /** * Test small Store & Folder functions that manage folders & namespace */ - public void testStoreFoldersFunctions() { + public void testStoreFoldersFunctions() throws MessagingException { // getPersonalNamespaces() always returns INBOX folder - Folder[] folders = mStore.getAllFolders(); + Folder[] folders = mStore.updateFolders(); assertEquals(1, folders.length); assertSame(mFolder, folders[0]); @@ -326,8 +326,8 @@ public class Pop3StoreUnitTests extends InstrumentationTestCase { /** * Lightweight test to confirm that POP3 hasn't implemented any folder roles yet. */ - public void testNoFolderRolesYet() { - Folder[] remoteFolders = mStore.getAllFolders(); + public void testNoFolderRolesYet() throws MessagingException { + Folder[] remoteFolders = mStore.updateFolders(); for (Folder folder : remoteFolders) { assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole()); } diff --git a/tests/src/com/android/email/provider/ProviderTestUtils.java b/tests/src/com/android/email/provider/ProviderTestUtils.java index bfc7cc328..ecdffb447 100644 --- a/tests/src/com/android/email/provider/ProviderTestUtils.java +++ b/tests/src/com/android/email/provider/ProviderTestUtils.java @@ -117,9 +117,12 @@ public class ProviderTestUtils extends Assert { Context context) { return setupMailbox(name, accountId, saveIt, context, Mailbox.TYPE_MAIL); } - public static Mailbox setupMailbox(String name, long accountId, boolean saveIt, Context context, int type) { + return setupMailbox(name, accountId, saveIt, context, type, '/'); + } + public static Mailbox setupMailbox(String name, long accountId, boolean saveIt, + Context context, int type, char delimiter) { Mailbox box = new Mailbox(); box.mDisplayName = name; @@ -128,7 +131,7 @@ public class ProviderTestUtils extends Assert { box.mParentKey = 4; box.mAccountKey = accountId; box.mType = type; - box.mDelimiter = 1; + box.mDelimiter = delimiter; box.mSyncKey = "sync-key-" + name; box.mSyncLookback = 2; box.mSyncInterval = EmailContent.Account.CHECK_INTERVAL_NEVER; diff --git a/tests/src/com/android/email/provider/ProviderTests.java b/tests/src/com/android/email/provider/ProviderTests.java index 4b91d7f80..73d1d05b4 100644 --- a/tests/src/com/android/email/provider/ProviderTests.java +++ b/tests/src/com/android/email/provider/ProviderTests.java @@ -2093,22 +2093,55 @@ public class ProviderTests extends ProviderTestCase2 { assertEquals(-1, Account.getAccountIdForMessageId(c, 12345)); } - public void testGetAccountMailboxFromMessageId() { + public void testGetMailboxForMessageId() { final Context c = mMockContext; - Account a = ProviderTestUtils.setupAccount("acct", true, c); - Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a.mId, true, c, Mailbox.TYPE_MAIL); - Mailbox b2 = ProviderTestUtils.setupMailbox("box2", a.mId, true, c, Mailbox.TYPE_MAIL); + Mailbox b1 = ProviderTestUtils.setupMailbox("box1", 1, true, c, Mailbox.TYPE_MAIL); + Mailbox b2 = ProviderTestUtils.setupMailbox("box2", 1, true, c, Mailbox.TYPE_MAIL); Message m1 = createMessage(c, b1, false, false); Message m2 = createMessage(c, b2, false, false); - ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m1.mId)); - ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m2.mId)); - // Restore the mailboxes, since the unread & total counts will have changed - b1 = Mailbox.restoreMailboxWithId(c, b1.mId); - b2 = Mailbox.restoreMailboxWithId(c, b2.mId); ProviderTestUtils.assertMailboxEqual("x", b1, Mailbox.getMailboxForMessageId(c, m1.mId)); ProviderTestUtils.assertMailboxEqual("x", b2, Mailbox.getMailboxForMessageId(c, m2.mId)); } + public void testRestoreMailboxWithId() { + final Context c = mMockContext; + Mailbox testMailbox; + + testMailbox = ProviderTestUtils.setupMailbox("box1", 1, true, c, Mailbox.TYPE_MAIL); + ProviderTestUtils.assertMailboxEqual( + "x", testMailbox, Mailbox.restoreMailboxWithId(c, testMailbox.mId)); + testMailbox = ProviderTestUtils.setupMailbox("box2", 1, true, c, Mailbox.TYPE_MAIL); + ProviderTestUtils.assertMailboxEqual( + "x", testMailbox, Mailbox.restoreMailboxWithId(c, testMailbox.mId)); + // Unknown IDs + assertNull(Mailbox.restoreMailboxWithId(c, 8)); + assertNull(Mailbox.restoreMailboxWithId(c, -1)); + assertNull(Mailbox.restoreMailboxWithId(c, Long.MAX_VALUE)); + } + + public void testRestoreMailboxForPath() { + final Context c = mMockContext; + Mailbox testMailbox; + testMailbox = ProviderTestUtils.setupMailbox("a/b/c/box", 1, true, c, Mailbox.TYPE_MAIL); + ProviderTestUtils.assertMailboxEqual( + "x", testMailbox, Mailbox.restoreMailboxForPath(c, 1, "a/b/c/box")); + // Same name, different account; no match + assertNull(Mailbox.restoreMailboxForPath(c, 2, "a/b/c/box")); + // Substring; no match + assertNull(Mailbox.restoreMailboxForPath(c, 1, "a/b/c")); + // Wild cards not supported; no match + assertNull(Mailbox.restoreMailboxForPath(c, 1, "a/b/c/%")); + } + + public void testGetAccountForMessageId() { + final Context c = mMockContext; + Account a = ProviderTestUtils.setupAccount("acct", true, c); + Message m1 = ProviderTestUtils.setupMessage("1", a.mId, 1, true, true, c, false, false); + Message m2 = ProviderTestUtils.setupMessage("1", a.mId, 2, true, true, c, false, false); + ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m1.mId)); + ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m2.mId)); + } + public void testGetAccountGetInboxIdTest() { final Context c = mMockContext;