Convert FolderSelectionDialog to a DialogFragment

Also clean it up to use loaders

b/11538664

Change-Id: I5810f0c4423c0c30d9785723672b3535eebcf998
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 6f5a3e6..99e3932 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -110,7 +110,6 @@
         mFolder = folder;
         mContext = mActivity.getActivityContext();
         mUpdater = activity.getConversationUpdater();
-        FolderSelectionDialog.setDialogDismissed();
     }
 
     @Override
@@ -186,10 +185,10 @@
             }
             if (!cantMove) {
                 final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(
-                        mContext, acct, mUpdater, mSelectionSet.values(), true, mFolder,
+                        acct, mSelectionSet.values(), true, mFolder,
                         item.getItemId() == R.id.move_to);
                 if (dialog != null) {
-                    dialog.show();
+                    dialog.show(mActivity.getFragmentManager(), null);
                 }
             }
         } else if (itemId == R.id.move_to_inbox) {
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 8a7609f..9335957 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -1240,7 +1240,6 @@
         mPagerController = new ConversationPagerController(mActivity, this);
         mToastBar = (ActionableToastBar) mActivity.findViewById(R.id.toast_bar);
         attachActionBar();
-        FolderSelectionDialog.setDialogDismissed();
 
         mDrawIdler.setRootView(mActivity.getWindow().getDecorView());
 
@@ -1429,12 +1428,11 @@
         } else if (id == R.id.manage_folders_item) {
             Utils.showManageFolder(mActivity.getActivityContext(), mAccount);
         } else if (id == R.id.move_to || id == R.id.change_folders) {
-            final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(
-                    mActivity.getActivityContext(), mAccount, this,
+            final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(mAccount,
                     Conversation.listOf(mCurrentConversation), isBatch, mFolder,
                     id == R.id.move_to);
             if (dialog != null) {
-                dialog.show();
+                dialog.show(mActivity.getFragmentManager(), null);
             }
         } else if (id == R.id.move_to_inbox) {
             new AsyncTask<Void, Void, Folder>() {
diff --git a/src/com/android/mail/ui/FolderOperation.java b/src/com/android/mail/ui/FolderOperation.java
index 3dc61b8..d7382a3 100644
--- a/src/com/android/mail/ui/FolderOperation.java
+++ b/src/com/android/mail/ui/FolderOperation.java
@@ -17,6 +17,9 @@
 
 package com.android.mail.ui;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import com.android.mail.providers.Folder;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
@@ -26,11 +29,11 @@
 import java.util.Collections;
 import java.util.HashSet;
 
-public class FolderOperation {
+public class FolderOperation implements Parcelable {
     /** An immutable, empty conversation list */
     public static final Collection<FolderOperation> EMPTY = Collections.emptyList();
-    public Folder mFolder;
-    public boolean mAdd;
+    public final Folder mFolder;
+    public final boolean mAdd;
 
     public FolderOperation(Folder folder, Boolean operation) {
         mAdd = operation;
@@ -39,10 +42,10 @@
 
     /**
      * Get all the unique folders associated with a set of folder operations.
-     * @param ops
-     * @return
+     * @param ops Operations to inspect
+     * @return ArrayList of Folder objects
      */
-    public final static ArrayList<Folder> getFolders(Collection<FolderOperation> ops) {
+    public static ArrayList<Folder> getFolders(Collection<FolderOperation> ops) {
         HashSet<Folder> folders = new HashSet<Folder>();
         for (FolderOperation op : ops) {
             folders.add(op.mFolder);
@@ -77,4 +80,34 @@
         }
         return false;
     }
+
+    // implements Parcelable
+
+    FolderOperation(Parcel in) {
+        mAdd = in.readByte() != 0;
+        mFolder = in.readParcelable(getClass().getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte((byte)(mAdd ? 1 : 0));
+        dest.writeParcelable(mFolder, 0);
+    }
+
+    public static final Creator<FolderOperation> CREATOR = new Creator<FolderOperation>() {
+        @Override
+        public FolderOperation createFromParcel(Parcel source) {
+            return new FolderOperation(source);
+        }
+
+        @Override
+        public FolderOperation[] newArray(int size) {
+            return new FolderOperation[size];
+        }
+    };
 }
diff --git a/src/com/android/mail/ui/FolderSelectionDialog.java b/src/com/android/mail/ui/FolderSelectionDialog.java
index 2885599..0e1ea1c 100644
--- a/src/com/android/mail/ui/FolderSelectionDialog.java
+++ b/src/com/android/mail/ui/FolderSelectionDialog.java
@@ -18,11 +18,10 @@
 package com.android.mail.ui;
 
 import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
+import android.app.Dialog;
+import android.app.DialogFragment;
 import android.content.DialogInterface.OnClickListener;
-import android.content.DialogInterface.OnDismissListener;
-import android.os.AsyncTask;
+import android.os.Bundle;
 import android.view.View;
 import android.widget.AdapterView;
 
@@ -32,117 +31,87 @@
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.utils.LogTag;
-import com.android.mail.utils.LogUtils;
 
+import java.util.Arrays;
 import java.util.Collection;
 
-public abstract class FolderSelectionDialog implements OnClickListener, OnDismissListener {
+public abstract class FolderSelectionDialog extends DialogFragment implements OnClickListener {
     protected static final String LOG_TAG = LogTag.getLogTag();
-    private static boolean sDialogShown;
 
-    protected AlertDialog mDialog;
-    protected final ConversationUpdater mUpdater;
-    protected final SeparatedFolderListAdapter mAdapter;
-    protected final Collection<Conversation> mTarget;
-    protected final boolean mBatch;
-    protected final QueryRunner mRunner;
-    protected final Account mAccount;
-    protected final AlertDialog.Builder mBuilder;
-    protected final Folder mCurrentFolder;
+    private static final String ARG_FOLDER_TAG = "folder";
+    private static final String ARG_ACCOUNT_TAG = "account";
+    private static final String ARG_BATCH_TAG = "batch";
+    private static final String ARG_TARGET_TAG = "target";
 
-    public static FolderSelectionDialog getInstance(final Context context, final Account account,
-            final ConversationUpdater updater, final Collection<Conversation> target,
-            final boolean isBatch, final Folder currentFolder, final boolean isMoveTo) {
-        if (sDialogShown) {
-            return null;
-        }
+    protected SeparatedFolderListAdapter mAdapter;
+    protected Collection<Conversation> mTarget;
+    // True for CAB mode
+    protected boolean mBatch;
+    protected Account mAccount;
+    protected Folder mCurrentFolder;
+    protected int mTitleId;
 
+    public static FolderSelectionDialog getInstance(final Account account,
+            final Collection<Conversation> target, final boolean isBatch,
+            final Folder currentFolder, final boolean isMoveTo) {
         /*
          * TODO: This method should only be called with isMoveTo=true if this capability is not
          * present on the account, so we should be able to remove the check here.
          */
+        final FolderSelectionDialog f;
         if (isMoveTo || !account.supportsCapability(
                 UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV)) {
-            return new SingleFolderSelectionDialog(
-                    context, account, updater, target, isBatch, currentFolder);
+            f = new SingleFolderSelectionDialog();
         } else {
-            return new MultiFoldersSelectionDialog(
-                    context, account, updater, target, isBatch, currentFolder);
+            f = new MultiFoldersSelectionDialog();
         }
+        final Bundle args = new Bundle(4);
+        args.putParcelable(ARG_FOLDER_TAG, currentFolder);
+        args.putParcelable(ARG_ACCOUNT_TAG, account);
+        args.putBoolean(ARG_BATCH_TAG, isBatch);
+        args.putParcelableArray(ARG_TARGET_TAG, target.toArray(new Conversation[target.size()]));
+        f.setArguments(args);
+        return f;
     }
 
-    public static void setDialogDismissed() {
-        LogUtils.d(LOG_TAG, "Folder Selection dialog dismissed");
-        sDialogShown = false;
-    }
-
-    // TODO: use a loader instead
-    @Deprecated
-    protected abstract void updateAdapterInBackground(Context context);
-
     protected abstract void onListItemClick(int position);
 
-    protected FolderSelectionDialog(final Context context, final Account account,
-            final ConversationUpdater updater, final Collection<Conversation> target,
-            final boolean isBatch, final Folder currentFolder) {
-        mUpdater = updater;
-        mTarget = target;
-        mBatch = isBatch;
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setNegativeButton(R.string.cancel, this);
-        mAccount = account;
-        mBuilder = builder;
-        mCurrentFolder = currentFolder;
+    @Override
+    public void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
         mAdapter = new SeparatedFolderListAdapter();
-        mRunner = new QueryRunner(context);
+
+        final Bundle args = getArguments();
+
+        mCurrentFolder = args.getParcelable(ARG_FOLDER_TAG);
+        mAccount = args.getParcelable(ARG_ACCOUNT_TAG);
+        mBatch = args.getBoolean(ARG_BATCH_TAG);
+        mTarget = Arrays.asList((Conversation[])args.getParcelableArray(ARG_TARGET_TAG));
     }
 
-    public void show() {
-        sDialogShown = true;
-        // TODO: use a loader instead
-        mRunner.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-    }
-
-    protected void showInternal() {
-        mDialog.show();
-        mDialog.setOnDismissListener(this);
-        mDialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                @Override
+    @Override
+    public Dialog onCreateDialog(final Bundle savedInstanceState) {
+        final AlertDialog dialog = new AlertDialog.Builder(getActivity())
+                .setNegativeButton(R.string.cancel, this)
+                .setPositiveButton(R.string.ok, this)
+                .setAdapter(mAdapter, this)
+                .setTitle(mTitleId)
+                .create();
+        dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 onListItemClick(position);
             }
         });
+        return dialog;
     }
 
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        FolderSelectionDialog.setDialogDismissed();
-    }
-
-    /**
-     * Class to query the Folder list database in the background and update the
-     * adapter with an open cursor.
-     */
-    // TODO: use a loader instead
-    @Deprecated
-    private class QueryRunner extends AsyncTask<Void, Void, Void> {
-        private final Context mContext;
-
-        private QueryRunner(final Context context) {
-            mContext = context;
+    protected ConversationUpdater getConversationUpdater() {
+        if (!isResumed()) {
+            throw new IllegalStateException(
+                    "Tried to update conversations while fragment is not running");
         }
-
-        @Override
-        protected Void doInBackground(Void... v) {
-            updateAdapterInBackground(mContext);
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void v) {
-            mDialog = mBuilder.create();
-            showInternal();
-        }
+        final ControllableActivity activity = (ControllableActivity)getActivity();
+        return activity.getConversationUpdater();
     }
 }
diff --git a/src/com/android/mail/ui/MultiFoldersSelectionDialog.java b/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
index 3fb378b..bbd9f08 100644
--- a/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
+++ b/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
@@ -17,13 +17,17 @@
 
 package com.android.mail.ui;
 
+import android.app.AlertDialog;
+import android.app.LoaderManager;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.DialogInterface;
+import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 
 import com.android.mail.R;
-import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
@@ -33,7 +37,6 @@
 import com.google.common.collect.ImmutableSet;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -43,75 +46,105 @@
  * the user to mark folders to assign that conversation to.
  */
 public class MultiFoldersSelectionDialog extends FolderSelectionDialog {
-    private final boolean mSingle;
+    private boolean mSingle;
     private final HashMap<Uri, FolderOperation> mOperations;
 
-    /**
-     * Create a new {@link MultiFoldersSelectionDialog}. It is displayed when
-     * the {@link #show()} method is called.
-     * @param context
-     * @param account the current account
-     * @param updater
-     * @param target conversations that are impacted
-     * @param isBatch whether the dialog is shown during Contextual Action Bar
-     *            (CAB) mode
-     * @param currentFolder the current folder that the
-     *            {@link FolderListFragment} is showing
-     */
-    public MultiFoldersSelectionDialog(final Context context, final Account account,
-            final ConversationUpdater updater, final Collection<Conversation> target,
-            final boolean isBatch, final Folder currentFolder) {
-        super(context, account, updater, target, isBatch, currentFolder);
-        mSingle = !account
-                .supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
+    public MultiFoldersSelectionDialog() {
         mOperations = new HashMap<Uri, FolderOperation>();
+    }
 
-        mBuilder.setTitle(R.string.change_folders_selection_dialog_title);
-        mBuilder.setPositiveButton(R.string.ok, this);
+    private static final int FOLDER_LOADER_ID = 0;
+    private static final String FOLDER_QUERY_URI_TAG = "folderQueryUri";
+
+    private static final String SAVESTATE_OPERATIONS_TAG = "operations";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mSingle = !mAccount
+                .supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
+        mTitleId = R.string.change_folders_selection_dialog_title;
+
+        if (savedInstanceState != null) {
+            final FolderOperation[] savedOps = (FolderOperation[])
+                    savedInstanceState.getParcelableArray(SAVESTATE_OPERATIONS_TAG);
+            for (final FolderOperation op : savedOps) {
+                mOperations.put(op.mFolder.folderUri.fullUri, op);
+            }
+        }
+
+        final Bundle args = new Bundle(1);
+        args.putParcelable(FOLDER_QUERY_URI_TAG, !Utils.isEmpty(mAccount.fullFolderListUri) ?
+                mAccount.fullFolderListUri : mAccount.folderListUri);
+        final Context loaderContext = getActivity().getApplicationContext();
+        getLoaderManager().initLoader(FOLDER_LOADER_ID, args,
+                new LoaderManager.LoaderCallbacks<Cursor>() {
+                    @Override
+                    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+                        final Uri queryUri = args.getParcelable(FOLDER_QUERY_URI_TAG);
+                        return new CursorLoader(loaderContext, queryUri,
+                                UIProvider.FOLDERS_PROJECTION, null, null, null);
+                    }
+
+                    @Override
+                    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+                        final Context context = getActivity();
+                        if (data == null || context == null) {
+                            return;
+                        }
+                        final AlertDialog dialog = (AlertDialog) getDialog();
+                        // The number of view types changes here, so we have to reset the listview's
+                        // adapter.
+                        dialog.getListView().setAdapter(null);
+
+                        final HashSet<String> checked = new HashSet<String>();
+                        for (final Conversation conversation : mTarget) {
+                            final List<Folder> rawFolders = conversation.getRawFolders();
+                            if (rawFolders != null && rawFolders.size() > 0) {
+                                // Parse the raw folders and get all the uris.
+                                checked.addAll(Arrays.asList(Folder.getUriArray(rawFolders)));
+                            } else {
+                                // There are no folders for this conversation, so it must
+                                // belong to the folder we are currently looking at.
+                                checked.add(mCurrentFolder.folderUri.fullUri.toString());
+                            }
+                        }
+                        for (final Uri folderUri : mOperations.keySet()) {
+                            // TODO: this makes these entries sort to the top after rotation
+                            checked.add(folderUri.toString());
+                        }
+                        // TODO(mindyp) : bring this back in UR8 when Email providers
+                        // will have divided folder sections.
+                        /* final String[] headers = mContext.getResources()
+                                .getStringArray(R.array.moveto_folder_sections);
+                         // Currently, the number of adapters are assumed to match the
+                         // number of headers in the string array.
+                         mAdapter.addSection(new SystemFolderSelectorAdapter(mContext,
+                         foldersCursor, checked, R.layout.multi_folders_view, null));
+
+                        // TODO(mindyp): we currently do not support frequently moved to
+                        // folders, at headers[1]; need to define what that means.*/
+                        mAdapter.addSection(new AddableFolderSelectorAdapter(context,
+                                AddableFolderSelectorAdapter.filterFolders(data,
+                                        ImmutableSet.of(FolderType.INBOX_SECTION)), checked,
+                                R.layout.multi_folders_view, null));
+
+                        dialog.getListView().setAdapter(mAdapter);
+                    }
+
+                    @Override
+                    public void onLoaderReset(Loader<Cursor> loader) {
+                        mAdapter.clearSections();
+                    }
+                });
     }
 
     @Override
-    protected void updateAdapterInBackground(Context context) {
-        Cursor foldersCursor = null;
-        try {
-            foldersCursor = context.getContentResolver().query(
-                    !Utils.isEmpty(mAccount.fullFolderListUri) ? mAccount.fullFolderListUri
-                            : mAccount.folderListUri, UIProvider.FOLDERS_PROJECTION, null, null,
-                            null);
-            /** All the folders that this conversations is assigned to. */
-            final HashSet<String> checked = new HashSet<String>();
-            for (final Conversation conversation : mTarget) {
-                final List<Folder> rawFolders = conversation.getRawFolders();
-                if (rawFolders != null && rawFolders.size() > 0) {
-                    // Parse the raw folders and get all the uris.
-                    checked.addAll(Arrays.asList(Folder.getUriArray(rawFolders)));
-                } else {
-                    // There are no folders for this conversation, so it must
-                    // belong to the folder we are currently looking at.
-                    checked.add(mCurrentFolder.folderUri.fullUri.toString());
-                }
-            }
-            // TODO(mindyp) : bring this back in UR8 when Email providers
-            // will have divided folder sections.
-            /* final String[] headers = mContext.getResources()
-             .getStringArray(R.array.moveto_folder_sections);
-             // Currently, the number of adapters are assumed to match the
-             // number of headers in the string array.
-             mAdapter.addSection(new SystemFolderSelectorAdapter(mContext,
-             foldersCursor, checked, R.layout.multi_folders_view, null));
-
-            // TODO(mindyp): we currently do not support frequently moved to
-            // folders, at headers[1]; need to define what that means.*/
-            mAdapter.addSection(new AddableFolderSelectorAdapter(context,
-                    AddableFolderSelectorAdapter.filterFolders(foldersCursor,
-                            ImmutableSet.of(FolderType.INBOX_SECTION)), checked,
-                    R.layout.multi_folders_view, null));
-            mBuilder.setAdapter(mAdapter, MultiFoldersSelectionDialog.this);
-        } finally {
-            if (foldersCursor != null) {
-                foldersCursor.close();
-            }
-        }
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelableArray(SAVESTATE_OPERATIONS_TAG,
+                mOperations.values().toArray(new FolderOperation[mOperations.size()]));
     }
 
     @Override
@@ -128,7 +161,7 @@
      *
      * @param row The item being updated.
      */
-    private final void update(FolderSelectorAdapter.FolderRow row) {
+    private void update(FolderSelectorAdapter.FolderRow row) {
         final boolean add = !row.isPresent();
         if (mSingle) {
             if (!add) {
@@ -157,10 +190,8 @@
     public void onClick(DialogInterface dialog, int which) {
         switch (which) {
             case DialogInterface.BUTTON_POSITIVE:
-                if (mUpdater != null) {
-                    mUpdater.assignFolder(mOperations.values(), mTarget, mBatch,
-                            true /* showUndo */, false /* isMoveTo */);
-                }
+                getConversationUpdater().assignFolder(mOperations.values(), mTarget, mBatch,
+                        true /* showUndo */, false /* isMoveTo */);
                 break;
             case DialogInterface.BUTTON_NEGATIVE:
                 break;
diff --git a/src/com/android/mail/ui/SeparatedFolderListAdapter.java b/src/com/android/mail/ui/SeparatedFolderListAdapter.java
index 0fa4925..93f0c8b 100644
--- a/src/com/android/mail/ui/SeparatedFolderListAdapter.java
+++ b/src/com/android/mail/ui/SeparatedFolderListAdapter.java
@@ -35,6 +35,10 @@
         mSections.add(adapter);
     }
 
+    public void clearSections() {
+        mSections.clear();
+    }
+
     @Override
     public Object getItem(int position) {
         for (FolderSelectorAdapter adapter : mSections) {
@@ -66,7 +70,7 @@
         int total = 0;
         for (Adapter adapter : mSections)
             total += adapter.getViewTypeCount();
-        return total;
+        return total == 0 ? 1 : total;
     }
 
     @Override
diff --git a/src/com/android/mail/ui/SingleFolderSelectionDialog.java b/src/com/android/mail/ui/SingleFolderSelectionDialog.java
index 8f0d5cb..bc7170c 100644
--- a/src/com/android/mail/ui/SingleFolderSelectionDialog.java
+++ b/src/com/android/mail/ui/SingleFolderSelectionDialog.java
@@ -17,76 +17,91 @@
 
 package com.android.mail.ui;
 
+import android.app.AlertDialog;
+import android.app.LoaderManager;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.DialogInterface;
+import android.content.Loader;
 import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
 
 import com.android.mail.R;
-import com.android.mail.providers.Account;
-import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.ui.FolderSelectorAdapter.FolderRow;
 import com.android.mail.utils.Utils;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * Displays a folder selection dialog for the conversation provided. It allows
  * the user to switch a conversation from one folder to another.
  */
 public class SingleFolderSelectionDialog extends FolderSelectionDialog {
+    public SingleFolderSelectionDialog() {}
 
-    /**
-     * Create a new {@link SingleFolderSelectionDialog}. It is displayed when
-     * the {@link #show()} method is called.
-     * @param context
-     * @param account the current account
-     * @param updater
-     * @param target conversations that are impacted
-     * @param isBatch whether the dialog is shown during Contextual Action Bar
-     *            (CAB) mode
-     * @param currentFolder the current folder that the
-     *            {@link FolderListFragment} is showing
-     */
-    public SingleFolderSelectionDialog(final Context context, final Account account,
-            final ConversationUpdater updater, final Collection<Conversation> target,
-            final boolean isBatch, final Folder currentFolder) {
-        super(context, account, updater, target, isBatch, currentFolder);
-
-        mBuilder.setTitle(R.string.move_to_selection_dialog_title);
-    }
+    private static final int FOLDER_LOADER_ID = 0;
+    private static final String FOLDER_QUERY_URI_TAG = "folderQueryUri";
 
     @Override
-    protected void updateAdapterInBackground(Context context) {
-        Cursor foldersCursor = null;
-        try {
-            foldersCursor = context.getContentResolver().query(
-                    !Utils.isEmpty(mAccount.fullFolderListUri) ? mAccount.fullFolderListUri
-                            : mAccount.folderListUri, UIProvider.FOLDERS_PROJECTION, null,
-                    null, null);
-            // TODO(mindyp) : bring this back in UR8 when Email providers
-            // will have divided folder sections.
-            final String[] headers = context.getResources().getStringArray(
-                    R.array.moveto_folder_sections);
-            // Currently, the number of adapters are assumed to match the
-            // number of headers in the string array.
-            mAdapter.addSection(new SystemFolderSelectorAdapter(context, foldersCursor,
-                    R.layout.single_folders_view, headers[0], mCurrentFolder));
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
 
-            // TODO(mindyp): we currently do not support frequently moved to
-            // folders, at headers[1]; need to define what that means.*/
-            // TODO(pwestbro): determine if we need to call filterFolders
-            mAdapter.addSection(new UserFolderHierarchicalFolderSelectorAdapter(context,
-                    AddableFolderSelectorAdapter.filterFolders(foldersCursor, null),
-                    R.layout.single_folders_view, headers[2], mCurrentFolder));
-            mBuilder.setAdapter(mAdapter, SingleFolderSelectionDialog.this);
-        } finally {
-            if (foldersCursor != null) {
-                foldersCursor.close();
-            }
-        }
+        mTitleId = R.string.move_to_selection_dialog_title;
+
+        final Bundle args = new Bundle(1);
+        args.putParcelable(FOLDER_QUERY_URI_TAG, !Utils.isEmpty(mAccount.fullFolderListUri) ?
+                mAccount.fullFolderListUri : mAccount.folderListUri);
+        final Context loaderContext = getActivity().getApplicationContext();
+        getLoaderManager().initLoader(FOLDER_LOADER_ID, args,
+                new LoaderManager.LoaderCallbacks<Cursor>() {
+                    @Override
+                    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+                        final Uri queryUri = args.getParcelable(FOLDER_QUERY_URI_TAG);
+                        return new CursorLoader(loaderContext, queryUri,
+                                UIProvider.FOLDERS_PROJECTION, null, null, null);
+                    }
+
+                    @Override
+                    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+                        final Context context = getActivity();
+                        if (data == null || context == null) {
+                            return;
+                        }
+
+                        final AlertDialog dialog = (AlertDialog) getDialog();
+                        // The number of view types changes here, so we have to reset the ListView's
+                        // adapter.
+                        dialog.getListView().setAdapter(null);
+
+                        mAdapter.clearSections();
+                        // TODO(mindyp) : bring this back in UR8 when Email providers
+                        // will have divided folder sections.
+                        final String[] headers = context.getResources().getStringArray(
+                                R.array.moveto_folder_sections);
+                        // Currently, the number of adapters are assumed to match the
+                        // number of headers in the string array.
+                        mAdapter.addSection(new SystemFolderSelectorAdapter(context, data,
+                                R.layout.single_folders_view, headers[0], mCurrentFolder));
+
+                        // TODO(mindyp): we currently do not support frequently moved to
+                        // folders, at headers[1]; need to define what that means.*/
+                        // TODO(pwestbro): determine if we need to call filterFolders
+                        mAdapter.addSection(
+                                new UserFolderHierarchicalFolderSelectorAdapter(context,
+                                AddableFolderSelectorAdapter.filterFolders(data, null),
+                                R.layout.single_folders_view, headers[2], mCurrentFolder));
+
+                        dialog.getListView().setAdapter(mAdapter);
+                    }
+
+                    @Override
+                    public void onLoaderReset(Loader<Cursor> loader) {
+                        mAdapter.clearSections();
+                    }
+                });
     }
 
     @Override
@@ -98,8 +113,9 @@
             // Remove the current folder and add the new folder.
             ops.add(new FolderOperation(mCurrentFolder, false));
             ops.add(new FolderOperation(folder, true));
-            mUpdater.assignFolder(ops, mTarget, mBatch, true /* showUndo */, true /* isMoveTo */);
-            mDialog.dismiss();
+            getConversationUpdater()
+                    .assignFolder(ops, mTarget, mBatch, true /* showUndo */, true /* isMoveTo */);
+            dismiss();
         }
     }