Use MergedAdapter in FolderListFragment.

Instead of using one adapter that contained the accounts
and the folders, the adapter now contains two sub-adapters,
and AccountsAdapter and a FolderAdapter. Functionality
should be unchanged.

Change-Id: I18c945cdcf9519549ab3c2886be033b398af086c
diff --git a/src/com/android/mail/adapter/DrawerItem.java b/src/com/android/mail/adapter/DrawerItem.java
index 093bdd3..b74cba0 100644
--- a/src/com/android/mail/adapter/DrawerItem.java
+++ b/src/com/android/mail/adapter/DrawerItem.java
@@ -50,9 +50,9 @@
     private static final String LOG_TAG = LogTag.getLogTag();
     public final Folder mFolder;
     public final Account mAccount;
-    public final int mResource;
+    private final int mResource;
     /** True if the drawer item represents the current account, false otherwise */
-    public boolean mIsSelected;
+    private final boolean mIsSelected;
     /** Either {@link #VIEW_ACCOUNT}, {@link #VIEW_FOLDER} or {@link #VIEW_HEADER} */
     public final int mType;
     /** A normal folder, also a child, if a parent is specified. */
diff --git a/src/com/android/mail/browse/MergedAdapter.java b/src/com/android/mail/browse/MergedAdapter.java
index 0212095..9689dca 100644
--- a/src/com/android/mail/browse/MergedAdapter.java
+++ b/src/com/android/mail/browse/MergedAdapter.java
@@ -22,7 +22,6 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.ListAdapter;
-import android.widget.SpinnerAdapter;
 
 import java.util.Arrays;
 import java.util.List;
@@ -32,26 +31,27 @@
  *
  * @param <T> the class of each constituent adapter
  */
-public class MergedAdapter<T extends MergedAdapter.ListSpinnerAdapter> extends BaseAdapter {
+public class MergedAdapter<T extends ListAdapter> extends BaseAdapter {
 
     private List<T> mAdapters;
     private final DataSetObserver mObserver;
 
-    /**
-     * A Mergeable adapter must implement both ListAdapter and SpinnerAdapter to be useful in lists
-     * and spinners.
-     */
-    public interface ListSpinnerAdapter extends ListAdapter, SpinnerAdapter {
-    }
-
-    public static class LocalAdapterPosition<T extends ListSpinnerAdapter> {
-        public final T mAdapter;
-        public final int mLocalPosition;
+    public static class LocalAdapterPosition<T extends ListAdapter> {
+        private final T mAdapter;
+        private final int mLocalPosition;
 
         public LocalAdapterPosition(T adapter, int offset) {
             mAdapter = adapter;
             mLocalPosition = offset;
         }
+
+        public T getAdapter() {
+            return mAdapter;
+        }
+
+        public int getLocalPosition() {
+            return mLocalPosition;
+        }
     }
 
     public MergedAdapter() {
@@ -169,12 +169,6 @@
     }
 
     @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
-        return result.mAdapter.getDropDownView(result.mLocalPosition, convertView, parent);
-    }
-
-    @Override
     public boolean areAllItemsEnabled() {
         boolean enabled = true;
         for (T adapter : mAdapters) {
diff --git a/src/com/android/mail/browse/MultiAdapterSpinner.java b/src/com/android/mail/browse/MultiAdapterSpinner.java
deleted file mode 100644
index 07c9db5..0000000
--- a/src/com/android/mail/browse/MultiAdapterSpinner.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.browse;
-
-import com.android.mail.browse.MergedAdapter.ListSpinnerAdapter;
-import com.android.mail.browse.MergedAdapter.LocalAdapterPosition;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.FrameLayout;
-import android.widget.ListPopupWindow;
-import android.widget.ListView;
-
-
-/**
- * <p>A spinner-like widget that combines data and views from multiple adapters (via MergedAdapter)
- * and forwards certain events to those adapters. This widget also supports clickable but
- * unselectable dropdown items, useful when displaying extra items that should not affect spinner
- * selection state.</p>
- *
- * <p>The framework's Spinner widget can't be extended for this task because it uses a private list
- * adapter (which prevents setting multiple item types) and hides access to its popup, which is
- * useful for clients to know about (like when it's been opened).</p>
- *
- * <p>Clients must provide a set of adapters which the widget will use to load dropdown views,
- * receive callbacks, and load the selected item's view.</p>
- *
- * <p>Apps incorporating this widget must declare a custom attribute: "dropDownWidth" under the
- * "MultiAdapterSpinner" name as a "reference" format (see Gmail's attrs.xml file for an
- * example). This attribute controls the width of the dropdown, similar to the attribute in the
- * framework's Spinner widget.</p>
- *
- */
-public class MultiAdapterSpinner extends FrameLayout
-    implements AdapterView.OnItemClickListener, View.OnClickListener {
-
-    protected MergedAdapter<FancySpinnerAdapter> mAdapter;
-    protected ListPopupWindow mPopup;
-
-    private int mSelectedPosition = -1;
-    private Rect mTempRect = new Rect();
-
-    /**
-     * A basic adapter with some callbacks added so clients can be involved in spinner behavior.
-     */
-    public interface FancySpinnerAdapter extends ListSpinnerAdapter {
-        /**
-         * Whether or not an item at position should become the new selected spinner item and change
-         * the spinner item view.
-         */
-        boolean canSelect(int position);
-        /**
-         * Handle a click on an enabled item.
-         */
-        void onClick(int position);
-        /**
-         * Fired when the popup window is about to be displayed.
-         */
-        void onShowPopup();
-    }
-
-    private static class MergedSpinnerAdapter extends MergedAdapter<FancySpinnerAdapter> {
-        /**
-         * ListPopupWindow uses getView() but spinners return dropdown views in getDropDownView().
-         */
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            return super.getDropDownView(position, convertView, parent);
-        }
-    }
-
-    public MultiAdapterSpinner(Context context) {
-        this(context, null);
-    }
-
-    public MultiAdapterSpinner(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mAdapter = new MergedSpinnerAdapter();
-        mPopup = new ListPopupWindow(context, attrs);
-        mPopup.setAnchorView(this);
-        mPopup.setOnItemClickListener(this);
-        mPopup.setModal(true);
-        mPopup.setAdapter(mAdapter);
-    }
-
-    public void setAdapters(FancySpinnerAdapter... adapters) {
-        mAdapter.setAdapters(adapters);
-    }
-
-    public void setSelectedItem(final FancySpinnerAdapter adapter, final int position) {
-        int globalPosition = 0;
-        boolean found = false;
-
-        for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
-            ListSpinnerAdapter a = mAdapter.getSubAdapter(i);
-            if (a == adapter) {
-                globalPosition += position;
-                found = true;
-                break;
-            }
-            globalPosition += a.getCount();
-        }
-        if (found) {
-            if (adapter.canSelect(position)) {
-                removeAllViews();
-                View itemView = adapter.getView(position, null, this);
-                itemView.setClickable(true);
-                itemView.setOnClickListener(this);
-                addView(itemView);
-
-                if (position < adapter.getCount()) {
-                    mSelectedPosition = globalPosition;
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (!mPopup.isShowing()) {
-
-            for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
-                mAdapter.getSubAdapter(i).onShowPopup();
-            }
-
-            final int spinnerPaddingLeft = getPaddingLeft();
-            final Drawable background = mPopup.getBackground();
-            int bgOffset = 0;
-            if (background != null) {
-                background.getPadding(mTempRect);
-                bgOffset = -mTempRect.left;
-            }
-            mPopup.setHorizontalOffset(bgOffset + spinnerPaddingLeft);
-            mPopup.show();
-            mPopup.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-            mPopup.setSelection(mSelectedPosition);
-        }
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-
-        if (position != mSelectedPosition) {
-            final LocalAdapterPosition<FancySpinnerAdapter> result =
-                mAdapter.getAdapterOffsetForItem(position);
-
-            if (result.mAdapter.canSelect(result.mLocalPosition)) {
-                mSelectedPosition = position;
-            } else {
-                mPopup.clearListSelection();
-            }
-
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    result.mAdapter.onClick(result.mLocalPosition);
-                }
-            });
-        }
-
-        mPopup.dismiss();
-    }
-
-}
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 20528ec..28b696d 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -4185,10 +4185,6 @@
                     mHideMenuItems = true;
                     mActivity.invalidateOptionsMenu();
                     disableCabMode();
-                    final FolderListFragment folderListFragment = getFolderListFragment();
-                    if (folderListFragment != null) {
-                        folderListFragment.updateScroll();
-                    }
                 }
             } else {
                 if (mHideMenuItems && Float.compare(slideOffset, 0.f) == 0) {
@@ -4199,10 +4195,6 @@
                     mHideMenuItems = true;
                     mActivity.invalidateOptionsMenu();
                     disableCabMode();
-                    final FolderListFragment folderListFragment = getFolderListFragment();
-                    if (folderListFragment != null) {
-                        folderListFragment.updateScroll();
-                    }
                 }
             }
 
diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java
index f3ed68c..74c00ec 100644
--- a/src/com/android/mail/ui/FolderListFragment.java
+++ b/src/com/android/mail/ui/FolderListFragment.java
@@ -36,6 +36,7 @@
 import com.android.mail.R;
 import com.android.mail.adapter.DrawerItem;
 import com.android.mail.analytics.Analytics;
+import com.android.mail.browse.MergedAdapter;
 import com.android.mail.content.ObjectCursor;
 import com.android.mail.content.ObjectCursorLoader;
 import com.android.mail.providers.Account;
@@ -136,7 +137,12 @@
     private static final String BUNDLE_SELECTED_FOLDER = "flf-selected-folder";
     private static final String BUNDLE_SELECTED_TYPE = "flf-selected-type";
 
-    private FolderListFragmentCursorAdapter mCursorAdapter;
+    /** Adapter used by the list that wraps both the folder adapter and the accounts adapter. */
+    private MergedAdapter<ListAdapter> mMergedAdapter;
+    /** Adapter containing the list of accounts. */
+    private AccountsAdapter mAccountsAdapter;
+    /** Adapter containing the list of folders and, optionally, headers and the wait view. */
+    private FolderListFragmentCursorAdapter mFolderAdapter;
     /** Observer to wait for changes to the current folder so we can change the selected folder */
     private FolderObserver mFolderObserver = null;
     /** Listen for account changes. */
@@ -157,6 +163,9 @@
     private Account mNextAccount = null;
     /** The folder we will change to once the drawer (if any) is closed */
     private Folder mNextFolder = null;
+    /** Watcher for tracking and receiving unread counts for mail */
+    private FolderWatcher mFolderWatcher = null;
+    private boolean mRegistered = false;
 
     /**
      * Constructor needs to be public to handle orientation changes and activity lifecycle events.
@@ -174,7 +183,7 @@
         sb.append(" parent=");
         sb.append(mParentFolder);
         sb.append(" adapterCount=");
-        sb.append(mCursorAdapter != null ? mCursorAdapter.getCount() : -1);
+        sb.append(mMergedAdapter != null ? mMergedAdapter.getCount() : -1);
         sb.append("}");
         return sb.toString();
     }
@@ -266,12 +275,15 @@
         // mActivity being initialized.
         final Folder selectedFolder;
         if (mParentFolder != null) {
-            mCursorAdapter = new HierarchicalFolderListAdapter(null, mParentFolder);
+            mFolderAdapter = new HierarchicalFolderListAdapter(null, mParentFolder);
             selectedFolder = mActivity.getHierarchyFolder();
         } else {
-            mCursorAdapter = new FolderListAdapter(mIsDivided);
+            mFolderAdapter = new FolderAdapter(mIsDivided);
             selectedFolder = currentFolder;
         }
+
+        mAccountsAdapter = new AccountsAdapter();
+
         // Is the selected folder fresher than the one we have restored from a bundle?
         if (selectedFolder != null
                 && !selectedFolder.folderUri.equals(mSelectedFolderUri)) {
@@ -294,7 +306,13 @@
             mAllAccountsObserver = new AllAccountObserver(){
                 @Override
                 public void onChanged(Account[] allAccounts) {
-                    mCursorAdapter.notifyAllAccountsChanged();
+                    if (!mRegistered && mAccountController != null) {
+                        // TODO(viki): Round-about way of setting the watcher. http://b/8750610
+                        mAccountController.setFolderWatcher(mFolderWatcher);
+                        mRegistered = true;
+                    }
+                    mFolderWatcher.updateAccountList(getAllAccounts());
+                    mAccountsAdapter.rebuildAccountList();
                 }
             };
             mAllAccountsObserver.initialize(accountController);
@@ -326,7 +344,13 @@
 
         mListView.setChoiceMode(getListViewChoiceMode());
 
-        setListAdapter(mCursorAdapter);
+        mMergedAdapter = new MergedAdapter<ListAdapter>();
+        mMergedAdapter.setAdapters(mAccountsAdapter, mFolderAdapter);
+
+        mFolderWatcher = new FolderWatcher(mActivity, mAccountsAdapter);
+        mFolderWatcher.updateAccountList(getAllAccounts());
+
+        setListAdapter(mMergedAdapter);
     }
 
     /**
@@ -398,8 +422,8 @@
 
     @Override
     public void onDestroyView() {
-        if (mCursorAdapter != null) {
-            mCursorAdapter.destroy();
+        if (mFolderAdapter != null) {
+            mFolderAdapter.destroy();
         }
         // Clear the adapter.
         setListAdapter(null);
@@ -428,10 +452,10 @@
     }
 
     private Folder getDefaultInbox(Account account) {
-        if (account == null || mCursorAdapter == null) {
+        if (account == null || mFolderWatcher == null) {
             return null;
         }
-        return mCursorAdapter.getDefaultInbox(account);
+        return mFolderWatcher.getDefaultInbox(account);
     }
 
     private void changeAccount(final Account account) {
@@ -455,7 +479,7 @@
         if (item instanceof DrawerItem) {
             final DrawerItem drawerItem = (DrawerItem) item;
             // Could be a folder or account.
-            final int itemType = mCursorAdapter.getItemType(drawerItem);
+            final int itemType = drawerItem.mType;
             if (itemType == DrawerItem.VIEW_ACCOUNT) {
                 // Account, so switch.
                 folder = null;
@@ -535,22 +559,22 @@
 
     @Override
     public void onLoadFinished(Loader<ObjectCursor<Folder>> loader, ObjectCursor<Folder> data) {
-        if (mCursorAdapter != null) {
+        if (mFolderAdapter != null) {
             if (loader.getId() == FOLDER_LIST_LOADER_ID) {
-                mCursorAdapter.setCursor(data);
+                mFolderAdapter.setCursor(data);
             } else if (loader.getId() == ALL_FOLDER_LIST_LOADER_ID) {
-                mCursorAdapter.setAllFolderListCursor(data);
+                mFolderAdapter.setAllFolderListCursor(data);
             }
         }
     }
 
     @Override
     public void onLoaderReset(Loader<ObjectCursor<Folder>> loader) {
-        if (mCursorAdapter != null) {
+        if (mFolderAdapter != null) {
             if (loader.getId() == FOLDER_LIST_LOADER_ID) {
-                mCursorAdapter.setCursor(null);
+                mFolderAdapter.setCursor(null);
             } else if (loader.getId() == ALL_FOLDER_LIST_LOADER_ID) {
-                mCursorAdapter.setAllFolderListCursor(null);
+                mFolderAdapter.setAllFolderListCursor(null);
             }
         }
     }
@@ -575,34 +599,22 @@
         void setCursor(ObjectCursor<Folder> cursor);
         /** Update the all folder list cursor with the cursor given here. */
         void setAllFolderListCursor(ObjectCursor<Folder> cursor);
-        /**
-         * Given an item, find the type of the item, which should only be {@link
-         * DrawerItem#VIEW_FOLDER} or {@link DrawerItem#VIEW_ACCOUNT}
-         * @return item the type of the item.
-         */
-        int getItemType(DrawerItem item);
-        /** Notify that the all accounts changed. */
-        void notifyAllAccountsChanged();
         /** Remove all observers and destroy the object. */
         void destroy();
         /** Notifies the adapter that the data has changed. */
         void notifyDataSetChanged();
-        /** Returns default inbox for this account. */
-        Folder getDefaultInbox(Account account);
-        /** Returns the index of the first selected item, or -1 if no selection */
-        int getSelectedPosition();
     }
 
     /**
      * An adapter for flat folder lists.
      */
-    private class FolderListAdapter extends BaseAdapter implements FolderListFragmentCursorAdapter {
+    private class FolderAdapter extends BaseAdapter implements FolderListFragmentCursorAdapter {
 
         private final RecentFolderObserver mRecentFolderObserver = new RecentFolderObserver() {
             @Override
             public void onChanged() {
                 if (!isCursorInvalid()) {
-                    recalculateList();
+                    rebuildFolderList();
                 }
             }
         };
@@ -619,17 +631,14 @@
         private ObjectCursor<Folder> mCursor = null;
         /** Cursor into the all folder list. This might be null. */
         private ObjectCursor<Folder> mAllFolderListCursor = null;
-        /** Watcher for tracking and receiving unread counts for mail */
-        private FolderWatcher mFolderWatcher = null;
-        private boolean mRegistered = false;
 
         /**
-         * Creates a {@link FolderListAdapter}.This is a list of all the accounts and folders.
+         * Creates a {@link FolderAdapter}. This is a list of all the accounts and folders.
          *
          * @param isDivided true if folder list is flat, false if divided by label group. See
          *                   the comments on {@link #mIsDivided} for more information
          */
-        public FolderListAdapter(boolean isDivided) {
+        public FolderAdapter(boolean isDivided) {
             super();
             mIsDivided = isDivided;
             final RecentFolderController controller = mActivity.getRecentFolderController();
@@ -638,19 +647,6 @@
             } else {
                 mRecentFolders = null;
             }
-            mFolderWatcher = new FolderWatcher(mActivity, this);
-            mFolderWatcher.updateAccountList(getAllAccounts());
-        }
-
-        @Override
-        public void notifyAllAccountsChanged() {
-            if (!mRegistered && mAccountController != null) {
-                // TODO(viki): Round-about way of setting the watcher. http://b/8750610
-                mAccountController.setFolderWatcher(mFolderWatcher);
-                mRegistered = true;
-            }
-            mFolderWatcher.updateAccountList(getAllAccounts());
-            recalculateList();
         }
 
         @Override
@@ -660,7 +656,7 @@
             final int type = item.mType;
             final boolean isSelected = item.isHighlighted(mSelectedFolderUri, mSelectedFolderType);
             if (type == DrawerItem.VIEW_FOLDER) {
-                mListView.setItemChecked(position, isSelected);
+                mListView.setItemChecked(mAccountsAdapter.getCount() + position, isSelected);
             }
             // If this is the current folder, also check to verify that the unread count
             // matches what the action bar shows.
@@ -696,10 +692,6 @@
             return drawerItem != null && drawerItem.isItemEnabled();
         }
 
-        private Uri getCurrentAccountUri() {
-            return mCurrentAccount == null ? Uri.EMPTY : mCurrentAccount.uri;
-        }
-
         @Override
         public boolean areAllItemsEnabled() {
             // We have headers and thus some items are not enabled.
@@ -728,46 +720,20 @@
         /**
          * Responsible for verifying mCursor, and ensuring any recalculate
          * conditions are met. Also calls notifyDataSetChanged once it's finished
-         * populating {@link FolderListAdapter#mItemList}
+         * populating {@link com.android.mail.ui.FolderListFragment.FolderAdapter#mItemList}
          */
-        private void recalculateList() {
-            final List<DrawerItem> newFolderList = new ArrayList<DrawerItem>();
-            // Don't show accounts for single-account-based folder selection (i.e. widgets)
-            if (!mHideAccounts) {
-                recalculateListAccounts(newFolderList);
-            }
-            recalculateListFolders(newFolderList);
-            mItemList = newFolderList;
+        private void rebuildFolderList() {
+            mItemList = recalculateListFolders();
             // Ask the list to invalidate its views.
             notifyDataSetChanged();
         }
 
         /**
-         * Recalculates the accounts if not null and adds them to the list.
-         *
-         * @param itemList List of drawer items to populate
-         */
-        private void recalculateListAccounts(List<DrawerItem> itemList) {
-            final Account[] allAccounts = getAllAccounts();
-            // Add all accounts and then the current account
-            final Uri currentAccountUri = getCurrentAccountUri();
-            for (final Account account : allAccounts) {
-                final int unreadCount = mFolderWatcher.getUnreadCount(account);
-                itemList.add(DrawerItem.ofAccount(mActivity, account, unreadCount,
-                        currentAccountUri.equals(account.uri), mBidiFormatter));
-            }
-            if (mCurrentAccount == null) {
-                LogUtils.wtf(LOG_TAG, "recalculateListAccounts() with null current account.");
-            }
-        }
-
-        /**
          * Recalculates the system, recent and user label lists.
          * This method modifies all the three lists on every single invocation.
-         *
-         * @param itemList List of drawer items to populate
          */
-        private void recalculateListFolders(List<DrawerItem> itemList) {
+        private List<DrawerItem> recalculateListFolders() {
+            final List<DrawerItem> itemList = new ArrayList<DrawerItem>();
             // If we are waiting for folder initialization, we don't have any kinds of folders,
             // just the "Waiting for initialization" item. Note, this should only be done
             // when we're waiting for account initialization or initial sync.
@@ -775,7 +741,7 @@
                 if(!mCurrentAccount.isAccountReady()) {
                     itemList.add(DrawerItem.ofWaitView(mActivity, mBidiFormatter));
                 }
-                return;
+                return itemList;
             }
 
             if (!mIsDivided) {
@@ -788,7 +754,7 @@
                     }
                 } while (mCursor.moveToNext());
 
-                return;
+                return itemList;
             }
 
             // Otherwise, this is an adapter for a divided list.
@@ -843,6 +809,8 @@
 
             // Add the remaining folders.
             addFolderDivision(itemList, allFoldersList, R.string.all_folders_heading);
+
+            return itemList;
         }
 
         /**
@@ -852,7 +820,7 @@
          * @param destination List of drawer items to populate
          * @param source List of drawer items representing folders to add to the drawer
          * @param headerStringResource
-         *            {@link FolderListAdapter#NO_HEADER_RESOURCE} if no header
+         *            {@link FolderAdapter#NO_HEADER_RESOURCE} if no header
          *            is required, or res-id otherwise. The integer is interpreted as the string
          *            for the header's title.
          */
@@ -909,13 +877,15 @@
         @Override
         public void setCursor(ObjectCursor<Folder> cursor) {
             mCursor = cursor;
-            recalculateList();
+            mAccountsAdapter.rebuildAccountList();
+            rebuildFolderList();
         }
 
         @Override
         public void setAllFolderListCursor(final ObjectCursor<Folder> cursor) {
             mAllFolderListCursor = cursor;
-            recalculateList();
+            mAccountsAdapter.rebuildAccountList();
+            rebuildFolderList();
         }
 
         @Override
@@ -937,33 +907,6 @@
         public final void destroy() {
             mRecentFolderObserver.unregisterAndDestroy();
         }
-
-        @Override
-        public Folder getDefaultInbox(Account account) {
-            if (mFolderWatcher != null) {
-                return mFolderWatcher.getDefaultInbox(account);
-            }
-            return null;
-        }
-
-        @Override
-        public int getItemType(DrawerItem item) {
-            return item.mType;
-        }
-
-        @Override
-        public int getSelectedPosition() {
-            for (int i = 0; i < mItemList.size(); i++) {
-                final DrawerItem item = (DrawerItem) getItem(i);
-                final boolean isSelected =
-                        item.isHighlighted(mSelectedFolderUri, mSelectedFolderType);
-                if (isSelected) {
-                    return i;
-                }
-            }
-
-            return -1;
-        }
     }
 
     private class HierarchicalFolderListAdapter extends ArrayAdapter<Folder>
@@ -1009,7 +952,7 @@
             }
             folderItemView.bind(folder, mDropHandler, mBidiFormatter);
             if (folder.folderUri.equals(mSelectedFolderUri)) {
-                getListView().setItemChecked(position, true);
+                getListView().setItemChecked(mAccountsAdapter.getCount() + position, true);
                 // If this is the current folder, also check to verify that the unread count
                 // matches what the action bar shows.
                 final boolean unreadCountDiffers = (mCurrentFolderForUnreadCheck != null)
@@ -1046,34 +989,70 @@
         public void destroy() {
             // Do nothing.
         }
+    }
 
-        @Override
-        public Folder getDefaultInbox(Account account) {
-            return null;
+    private class AccountsAdapter extends BaseAdapter {
+
+        private List<DrawerItem> mAccounts;
+
+        public AccountsAdapter() {
+            mAccounts = new ArrayList<DrawerItem>();
         }
 
-        @Override
-        public int getItemType(DrawerItem item) {
-            // Always returns folders for now.
-            return DrawerItem.VIEW_FOLDER;
-        }
-
-        @Override
-        public void notifyAllAccountsChanged() {
-            // Do nothing. We don't care about changes to all accounts.
-        }
-
-        @Override
-        public int getSelectedPosition() {
-            final int count = getCount();
-            for (int i = 0; i < count; i++) {
-                final Folder folder = getItem(i);
-                final boolean isSelected = folder.folderUri.equals(mSelectedFolderUri);
-                if (isSelected) {
-                    return i;
-                }
+        public void rebuildAccountList() {
+            if (!mHideAccounts) {
+                mAccounts = buildAccountList();
+                notifyDataSetChanged();
             }
-            return -1;
+        }
+
+        /**
+         * Builds the accounts adapter.
+         */
+        private List<DrawerItem> buildAccountList() {
+            final List<DrawerItem> accountList = new ArrayList<DrawerItem>();
+            final Account[] allAccounts = getAllAccounts();
+            // Add all accounts and then the current account
+            final Uri currentAccountUri = getCurrentAccountUri();
+            for (final Account account : allAccounts) {
+                final int unreadCount = mFolderWatcher.getUnreadCount(account);
+                accountList.add(DrawerItem.ofAccount(mActivity, account, unreadCount,
+                        currentAccountUri.equals(account.uri), mBidiFormatter));
+            }
+            if (mCurrentAccount == null) {
+                LogUtils.wtf(LOG_TAG, "buildAccountList() with null current account.");
+            }
+            return accountList;
+        }
+
+        private Uri getCurrentAccountUri() {
+            return mCurrentAccount == null ? Uri.EMPTY : mCurrentAccount.uri;
+        }
+
+        @Override
+        public int getCount() {
+            return mAccounts.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            // Is there an attempt made to access outside of the drawer item list?
+            if (position >= mAccounts.size()) {
+                return null;
+            } else {
+                return mAccounts.get(position);
+            }
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return getItem(position).hashCode();
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final DrawerItem item = (DrawerItem) getItem(position);
+            return item.getView(convertView, parent);
         }
     }
 
@@ -1107,18 +1086,8 @@
 
         mCurrentFolderForUnreadCheck = folder;
         mSelectedFolderUri = folder.folderUri;
-        if (mCursorAdapter != null && viewChanged) {
-            mCursorAdapter.notifyDataSetChanged();
-        }
-    }
-
-    public void updateScroll() {
-        final int selectedPosition = mCursorAdapter.getSelectedPosition();
-        if (selectedPosition >= 0) {
-            // TODO: setSelection() jumps the item to the top of the list "hiding" the accounts
-            // TODO: and smoothScrollToPosition() is too slow for lots of labels/folders
-            // It's called "setSelection" but it's really more like "jumpScrollToPosition"
-            // mListView.setSelection(selectedPosition);
+        if (mFolderAdapter != null && viewChanged) {
+            mFolderAdapter.notifyDataSetChanged();
         }
     }
 
@@ -1132,7 +1101,7 @@
         mCurrentAccount = account;
         if (changed) {
             // We no longer have proper folder objects. Let the new ones come in
-            mCursorAdapter.setCursor(null);
+            mFolderAdapter.setCursor(null);
             // If currentAccount is different from the one we set, restart the loader. Look at the
             // comment on {@link AbstractActivityController#restartOptionalLoader} to see why we
             // don't just do restartLoader.