Save sync options if folders are deleted and restore when reloaded

Bug: 5176640
Change-Id: I7c709998b65080545f468997e2df5bc00162a89f
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 9ba3697..c899cb2 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -30,6 +30,7 @@
 import com.android.emailcommon.provider.EmailContent.AccountColumns;
 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
 import com.android.emailcommon.provider.Mailbox;
+import com.android.emailcommon.service.SyncWindow;
 import com.android.emailcommon.utility.AttachmentUtilities;
 import com.android.emailcommon.utility.EmailAsyncTask;
 import com.android.emailcommon.utility.Utility;
@@ -38,6 +39,7 @@
 import com.android.exchange.Eas;
 import com.android.exchange.ExchangeService;
 import com.android.exchange.provider.MailboxUtilities;
+import com.google.common.annotations.VisibleForTesting;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -96,8 +98,10 @@
     private static final int MAILBOX_ID_COLUMNS_SERVER_ID = 1;
     private static final int MAILBOX_ID_COLUMNS_PARENT_SERVER_ID = 2;
 
-    private long mAccountId;
-    private String mAccountIdAsString;
+    @VisibleForTesting
+    long mAccountId;
+    @VisibleForTesting
+    String mAccountIdAsString;
     private String[] mBindArguments = new String[2];
     private ArrayList<ContentProviderOperation> mOperations =
         new ArrayList<ContentProviderOperation>();
@@ -165,6 +169,8 @@
                                 mAccount.mId), cv, null, null);
                         // Delete PIM data
                         ExchangeService.deleteAccountPIMData(mAccountId);
+                        // Save away any mailbox sync information that is NOT default
+                        saveMailboxSyncOptions();
                         // And only then, delete mailboxes
                         mContentResolver.delete(Mailbox.CONTENT_URI, ALL_BUT_ACCOUNT_MAILBOX,
                                 new String[] {Long.toString(mAccountId)});
@@ -237,6 +243,74 @@
         }
     }
 
+    private static class SyncOptions {
+        private final int mInterval;
+        private final int mLookback;
+
+        private SyncOptions(int interval, int lookback) {
+            mInterval = interval;
+            mLookback = lookback;
+        }
+    }
+
+    private static final String MAILBOX_STATE_SELECTION =
+        MailboxColumns.ACCOUNT_KEY + "=? AND (" + MailboxColumns.SYNC_INTERVAL + "!=" +
+            Account.CHECK_INTERVAL_NEVER + " OR " + Mailbox.SYNC_LOOKBACK + "!=" +
+            SyncWindow.SYNC_WINDOW_UNKNOWN + ")";
+
+    private static final String[] MAILBOX_STATE_PROJECTION = new String[] {
+        MailboxColumns.SERVER_ID, MailboxColumns.SYNC_INTERVAL, MailboxColumns.SYNC_LOOKBACK};
+    private static final int MAILBOX_STATE_SERVER_ID = 0;
+    private static final int MAILBOX_STATE_INTERVAL = 1;
+    private static final int MAILBOX_STATE_LOOKBACK = 2;
+    @VisibleForTesting
+    final HashMap<String, SyncOptions> mSyncOptionsMap = new HashMap<String, SyncOptions>();
+
+    /**
+     * For every mailbox in this account that has a non-default interval or lookback, save those
+     * values.
+     */
+    @VisibleForTesting
+    void saveMailboxSyncOptions() {
+        // Shouldn't be necessary, but...
+        mSyncOptionsMap.clear();
+        Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_STATE_PROJECTION,
+                MAILBOX_STATE_SELECTION, new String[] {mAccountIdAsString}, null);
+        if (c != null) {
+            try {
+                while (c.moveToNext()) {
+                    mSyncOptionsMap.put(c.getString(MAILBOX_STATE_SERVER_ID),
+                            new SyncOptions(c.getInt(MAILBOX_STATE_INTERVAL),
+                                    c.getInt(MAILBOX_STATE_LOOKBACK)));
+                }
+            } finally {
+                c.close();
+            }
+        }
+    }
+
+    /**
+     * For every set of saved mailbox sync options, try to find and restore those values
+     */
+    @VisibleForTesting
+    void restoreMailboxSyncOptions() {
+        try {
+            ContentValues cv = new ContentValues();
+            mBindArguments[1] = mAccountIdAsString;
+            for (String serverId: mSyncOptionsMap.keySet()) {
+                SyncOptions options = mSyncOptionsMap.get(serverId);
+                cv.put(MailboxColumns.SYNC_INTERVAL, options.mInterval);
+                cv.put(MailboxColumns.SYNC_LOOKBACK, options.mLookback);
+                mBindArguments[0] = serverId;
+                // If we match account and server id, set the sync options
+                mContentResolver.update(Mailbox.CONTENT_URI, cv, WHERE_SERVER_ID_AND_ACCOUNT,
+                        mBindArguments);
+            }
+        } finally {
+            mSyncOptionsMap.clear();
+        }
+    }
+
     public Mailbox addParser() throws IOException {
         String name = null;
         String serverId = null;
@@ -591,6 +665,11 @@
             mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
                     mBindArguments);
         }
+
+        // If we have saved options, restore them now
+        if (mInitialSync) {
+            restoreMailboxSyncOptions();
+        }
     }
 
     @Override
diff --git a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
index e0cabf5..1538f99 100644
--- a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
+++ b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
@@ -16,12 +16,15 @@
 
 package com.android.exchange.adapter;
 
+import android.content.ContentResolver;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.Mailbox;
+import com.android.emailcommon.service.SyncWindow;
 import com.android.exchange.EasSyncService;
 import com.android.exchange.provider.EmailContentSetupUtils;
 
-import android.test.suitebuilder.annotation.MediumTest;
-
 import java.io.IOException;
 import java.util.HashMap;
 
@@ -32,6 +35,10 @@
 @MediumTest
 public class FolderSyncParserTests extends SyncAdapterTestCase<EmailSyncAdapter> {
 
+    // We increment this to generate unique server id's
+    private int mServerIdCount = 0;
+    private final long mCreationTime = System.currentTimeMillis();
+
     public FolderSyncParserTests() {
         super();
     }
@@ -99,4 +106,102 @@
         // parent's parent is a mail type
         assertTrue(parser.isValidMailFolder(boxUnknownType, mailboxMap));
     }
+
+    private Mailbox setupBoxSync(int interval, int lookback, String serverId) {
+        // Don't save the box; just create it, and give it a server id
+        Mailbox box = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, false,
+                mProviderContext, Mailbox.TYPE_MAIL);
+        box.mSyncInterval = interval;
+        box.mSyncLookback = lookback;
+        if (serverId != null) {
+            box.mServerId = serverId;
+        } else {
+            box.mServerId = "serverId-" + mCreationTime + '-' + mServerIdCount++;
+        }
+        box.save(mProviderContext);
+        return box;
+    }
+
+    private boolean syncOptionsSame(Mailbox a, Mailbox b) {
+        if (a.mSyncInterval != b.mSyncInterval) return false;
+        if (a.mSyncLookback != b.mSyncLookback) return false;
+        return true;
+    }
+
+    public void testSaveAndRestoreMailboxSyncOptions() throws IOException {
+        EasSyncService service = getTestService();
+        EmailSyncAdapter adapter = new EmailSyncAdapter(service);
+        FolderSyncParser parser = new FolderSyncParser(getTestInputStream(), adapter);
+        mAccount.save(mProviderContext);
+
+        parser.mAccount = mAccount;
+        parser.mAccountId = mAccount.mId;
+        parser.mAccountIdAsString = Long.toString(mAccount.mId);
+        parser.mContext = mProviderContext;
+        parser.mContentResolver = mProviderContext.getContentResolver();
+
+        // Don't save the box; just create it, and give it a server id
+        Mailbox box1 = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                null);
+        Mailbox box2 = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                null);
+        Mailbox boxa = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_1_MONTH,
+                null);
+        Mailbox boxb = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_2_WEEKS,
+                null);
+        Mailbox boxc = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                null);
+        Mailbox boxd = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                null);
+        Mailbox boxe = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_1_DAY,
+                null);
+
+        // Save the options (for a, b, c, d, e);
+        parser.saveMailboxSyncOptions();
+        // There should be 5 entries in the map, and they should be the correct ones
+        assertNotNull(parser.mSyncOptionsMap.get(boxa.mServerId));
+        assertNotNull(parser.mSyncOptionsMap.get(boxb.mServerId));
+        assertNotNull(parser.mSyncOptionsMap.get(boxc.mServerId));
+        assertNotNull(parser.mSyncOptionsMap.get(boxd.mServerId));
+        assertNotNull(parser.mSyncOptionsMap.get(boxe.mServerId));
+
+        // Delete all the mailboxes in the account
+        ContentResolver cr = mProviderContext.getContentResolver();
+        cr.delete(Mailbox.CONTENT_URI, Mailbox.ACCOUNT_KEY + "=?",
+                new String[] {parser.mAccountIdAsString});
+
+        // Create new boxes, all with default values for interval & window
+        Mailbox box1x = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                box1.mServerId);
+        Mailbox box2x = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                box2.mServerId);
+        Mailbox boxax = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                boxa.mServerId);
+        Mailbox boxbx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                boxb.mServerId);
+        Mailbox boxcx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                boxc.mServerId);
+        Mailbox boxdx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                boxd.mServerId);
+        Mailbox boxex = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_UNKNOWN,
+                boxe.mServerId);
+
+        // Restore the sync options
+        parser.restoreMailboxSyncOptions();
+        box1x = Mailbox.restoreMailboxWithId(mProviderContext, box1x.mId);
+        box2x = Mailbox.restoreMailboxWithId(mProviderContext, box2x.mId);
+        boxax = Mailbox.restoreMailboxWithId(mProviderContext, boxax.mId);
+        boxbx = Mailbox.restoreMailboxWithId(mProviderContext, boxbx.mId);
+        boxcx = Mailbox.restoreMailboxWithId(mProviderContext, boxcx.mId);
+        boxdx = Mailbox.restoreMailboxWithId(mProviderContext, boxdx.mId);
+        boxex = Mailbox.restoreMailboxWithId(mProviderContext, boxex.mId);
+
+        assertTrue(syncOptionsSame(box1, box1x));
+        assertTrue(syncOptionsSame(box2, box2x));
+        assertTrue(syncOptionsSame(boxa, boxax));
+        assertTrue(syncOptionsSame(boxb, boxbx));
+        assertTrue(syncOptionsSame(boxc, boxcx));
+        assertTrue(syncOptionsSame(boxd, boxdx));
+        assertTrue(syncOptionsSame(boxe, boxex));
+    }
 }