Bug 889612: BookmarksListAdapter should be controlled by the BookmarksPage fragment. [r=bnicholson]
authorSriram Ramasubramanian <sriram@mozilla.com>
Tue, 02 Jul 2013 15:03:13 -0700
changeset 143375 22104d00bbdc131d564ae76165306d0f4bc2465d
parent 143374 e622ebfaad7415f970c60aeeba6d0df08dc8aefe
child 143376 d9832319249ba5de8f92f85160899f65bffbc243
push id25130
push userlrocha@mozilla.com
push dateWed, 21 Aug 2013 09:41:27 +0000
treeherdermozilla-central@b2486721572e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson
bugs889612
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 889612: BookmarksListAdapter should be controlled by the BookmarksPage fragment. [r=bnicholson]
mobile/android/base/Makefile.in
mobile/android/base/home/BookmarksListAdapter.java
mobile/android/base/home/BookmarksListView.java
mobile/android/base/home/BookmarksPage.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -203,16 +203,17 @@ FENNEC_JAVA_FILES = \
   gfx/SubdocumentScrollHelper.java \
   gfx/TextLayer.java \
   gfx/TextureGenerator.java \
   gfx/TextureReaper.java \
   gfx/TileLayer.java \
   gfx/TouchEventHandler.java \
   gfx/ViewTransform.java \
   gfx/VirtualLayer.java \
+  home/BookmarksListAdapter.java \
   home/BookmarksListView.java \
   home/BookmarksPage.java \
   home/BookmarkFolderView.java \
   home/BookmarkThumbnailView.java \
   home/BrowserSearch.java \
   home/HomeFragment.java \
   home/HomeListView.java \
   home/HomePager.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/home/BookmarksListAdapter.java
@@ -0,0 +1,226 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.home;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.LinkedList;
+
+/**
+ * Adapter to back the BookmarksListView with a list of bookmarks.
+ */
+class BookmarksListAdapter extends CursorAdapter {
+    private static final int VIEW_TYPE_ITEM = 0;
+    private static final int VIEW_TYPE_FOLDER = 1;
+
+    private static final int VIEW_TYPE_COUNT = 2;
+
+    // A listener that knows how to refresh the list for a given folder id.
+    // This is usually implemented by the enclosing fragment/activity.
+    public static interface OnRefreshFolderListener {
+        // The folder id to refresh the list with.
+        public void onRefreshFolder(int folderId);
+    }
+
+    // mParentStack holds folder id/title pairs that allow us to navigate
+    // back up the folder heirarchy.
+    private LinkedList<Pair<Integer, String>> mParentStack;
+
+    // Refresh folder listener.
+    private OnRefreshFolderListener mListener;
+
+    public BookmarksListAdapter(Context context, Cursor cursor) {
+        // Initializing with a null cursor.
+        super(context, cursor);
+
+        mParentStack = new LinkedList<Pair<Integer, String>>();
+
+        // Add the root folder to the stack
+        Pair<Integer, String> rootFolder = new Pair<Integer, String>(Bookmarks.FIXED_ROOT_ID, "");
+        mParentStack.addFirst(rootFolder);
+    }
+
+    // Refresh the current folder by executing a new task.
+    private void refreshCurrentFolder() {
+        if (mListener != null) {
+            mListener.onRefreshFolder(mParentStack.peek().first);
+        }
+    }
+
+    /**
+     * Moves to parent folder, if one exists.
+     */
+    public void moveToParentFolder() {
+        // If we're already at the root, we can't move to a parent folder
+        if (mParentStack.size() != 1) {
+            mParentStack.removeFirst();
+            refreshCurrentFolder();
+        }
+    }
+
+    /**
+     * Moves to child folder, given a folderId.
+     *
+     * @param folderId The id of the folder to show.
+     * @param folderTitle The title of the folder to show.
+     */
+    public void moveToChildFolder(int folderId, String folderTitle) {
+        Pair<Integer, String> folderPair = new Pair<Integer, String>(folderId, folderTitle);
+        mParentStack.addFirst(folderPair);
+        refreshCurrentFolder();
+    }
+
+    /**
+     * Set a listener that can refresh this adapter.
+     *
+     * @param listener The listener that can refresh the adapter.
+     */
+    public void setOnRefreshFolderListener(OnRefreshFolderListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        // The position also reflects the opened child folder row.
+        if (isShowingChildFolder()) {
+            if (position == 0) {
+                return VIEW_TYPE_FOLDER;
+            }
+
+            // Accounting for the folder view.
+            position--;
+        }
+
+        Cursor c = getCursor();
+
+        if (!c.moveToPosition(position)) {
+            throw new IllegalStateException("Couldn't move cursor to position " + position);
+        }
+
+        return getItemViewType(c);
+    }
+
+    /**
+     * Returns the type of the item at the given position in the cursor.
+     *
+     * @param cursor A cursor moved to the required position.
+     * @return The type of the item.
+     */
+    public int getItemViewType(Cursor cursor) {
+        if (cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER) {
+            return VIEW_TYPE_FOLDER;
+        }
+
+        // Default to returning normal item type.
+        return VIEW_TYPE_ITEM;
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return VIEW_TYPE_COUNT;
+    }
+
+    /**
+     * Get the title of the folder given a cursor moved to the position.
+     *
+     * @param context The context of the view.
+     * @param cursor A cursor moved to the required position.
+     * @return The title of the folder at the position.
+     */
+    public String getFolderTitle(Context context, Cursor c) {
+        String guid = c.getString(c.getColumnIndexOrThrow(Bookmarks.GUID));
+
+        // If we don't have a special GUID, just return the folder title from the DB.
+        if (guid == null || guid.length() == 12) {
+            return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
+        }
+
+        Resources res = context.getResources();
+
+        // Use localized strings for special folder names.
+        if (guid.equals(Bookmarks.FAKE_DESKTOP_FOLDER_GUID)) {
+            return res.getString(R.string.bookmarks_folder_desktop);
+        } else if (guid.equals(Bookmarks.MENU_FOLDER_GUID)) {
+            return res.getString(R.string.bookmarks_folder_menu);
+        } else if (guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID)) {
+            return res.getString(R.string.bookmarks_folder_toolbar);
+        } else if (guid.equals(Bookmarks.UNFILED_FOLDER_GUID)) {
+            return res.getString(R.string.bookmarks_folder_unfiled);
+        }
+
+        // If for some reason we have a folder with a special GUID, but it's not one of
+        // the special folders we expect in the UI, just return the title from the DB.
+        return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
+    }
+
+    /**
+     * @return true, if currently showing a child folder, false otherwise.
+     */
+    public boolean isShowingChildFolder() {
+        return (mParentStack.peek().first != Bookmarks.FIXED_ROOT_ID);
+    }
+
+    @Override
+    public int getCount() {
+        return super.getCount() + (isShowingChildFolder() ? 1 : 0);
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        // The position also reflects the opened child folder row.
+        if (isShowingChildFolder()) {
+            if (position == 0) {
+                BookmarkFolderView folder = (BookmarkFolderView) LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_folder_row, null);
+                folder.setText(mParentStack.peek().second);
+                folder.open();
+                return folder;
+            }
+
+            // Accounting for the folder view.
+            position--;
+        }
+
+        return super.getView(position, convertView, parent);
+    }
+
+    @Override
+    public void bindView(View view, Context context, Cursor cursor) {
+        final int viewType = getItemViewType(cursor);
+
+        if (viewType == VIEW_TYPE_ITEM) {
+            TwoLinePageRow row = (TwoLinePageRow) view;
+            row.updateFromCursor(cursor);
+        } else {
+            BookmarkFolderView row = (BookmarkFolderView) view;
+            row.setText(getFolderTitle(context, cursor));
+            row.close();
+        }
+    }
+
+    @Override
+    public View newView(Context context, Cursor cursor, ViewGroup parent) {
+        final int viewType = getItemViewType(cursor);
+
+        final int resId;
+        if (viewType == VIEW_TYPE_ITEM) {
+            resId = R.layout.home_item_row;
+        } else {
+            resId = R.layout.bookmark_folder_row;
+        }
+
+        return LayoutInflater.from(parent.getContext()).inflate(resId, null);
+    }
+}
--- a/mobile/android/base/home/BookmarksListView.java
+++ b/mobile/android/base/home/BookmarksListView.java
@@ -1,65 +1,43 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
-import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.util.GamepadUtils;
 
 import android.content.Context;
 import android.database.Cursor;
-import android.support.v4.widget.CursorAdapter;
 import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ListView;
 
-import java.util.LinkedList;
-
 /**
  * A ListView of bookmarks.
  */
 public class BookmarksListView extends HomeListView
                                implements AdapterView.OnItemClickListener{
     
     public static final String LOGTAG = "GeckoBookmarksListView";
 
-    // A listener that knows how to refresh the list for a given folder id.
-    // This is usually implemented by the enclosing fragment/activity.
-    public static interface OnRefreshFolderListener {
-
-        // The folder id to refresh the list with.
-        public void onRefreshFolder(int folderId);
-
-    }
-
-    // A cursor based adapter.
-    private BookmarksListAdapter mCursorAdapter = null;
-
     // The last motion event that was intercepted.
     private MotionEvent mMotionEvent;
 
     // The default touch slop.
     private int mTouchSlop;
 
-    // Refresh folder listener.
-    private OnRefreshFolderListener mListener;
-
     public BookmarksListView(Context context) {
         this(context, null);
     }
 
     public BookmarksListView(Context context, AttributeSet attrs) {
         this(context, attrs, android.R.attr.listViewStyle);
     }
 
@@ -69,32 +47,21 @@ public class BookmarksListView extends H
         // Scaled touch slop for this context.
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Intialize the adapter.
-        mCursorAdapter = new BookmarksListAdapter(getContext(), null);
-        setAdapter(mCursorAdapter);
-
         setOnItemClickListener(this);
         setOnKeyListener(GamepadUtils.getListItemClickDispatcher());
     }
 
     @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mCursorAdapter = null;
-        mListener = null;
-    }
-
-    @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
         switch(event.getAction() & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 // Store the event by obtaining a copy.
                 mMotionEvent = MotionEvent.obtain(event);
                 break;
             }
 
@@ -127,241 +94,43 @@ public class BookmarksListView extends H
         if (position < headerCount) {
             // The click is on a header, don't do anything.
             return;
         }
 
         // Absolute position for the adapter.
         position -= headerCount;
 
-        if (mCursorAdapter.isShowingChildFolder()) {
+        BookmarksListAdapter adapter = (BookmarksListAdapter) getAdapter();
+        if (adapter.isShowingChildFolder()) {
             if (position == 0) {
                 // If we tap on an opened folder, move back to parent folder.
-                mCursorAdapter.moveToParentFolder();
+                adapter.moveToParentFolder();
                 return;
             }
 
             // Accounting for the folder view.
             position--;
         }
 
-        final Cursor cursor = mCursorAdapter.getCursor();
+        final Cursor cursor = adapter.getCursor();
         if (cursor == null) {
             return;
         }
 
         cursor.moveToPosition(position);
 
         int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
         if (type == Bookmarks.TYPE_FOLDER) {
             // If we're clicking on a folder, update adapter to move to that folder
             final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
-            final String folderTitle = mCursorAdapter.getFolderTitle(cursor);
-            mCursorAdapter.moveToChildFolder(folderId, folderTitle);
+            final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor);
+            adapter.moveToChildFolder(folderId, folderTitle);
         } else {
             // Otherwise, just open the URL
             final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
             OnUrlOpenListener listener = getOnUrlOpenListener();
             if (listener != null) {
                 listener.onUrlOpen(url);
             }
         }
     }
-
-    public void refreshFromCursor(Cursor cursor) {
-        // This will update the cursorAdapter to use the new one if it already exists.
-        mCursorAdapter.swapCursor(cursor);
-    }
-
-    public void setOnRefreshFolderListener(OnRefreshFolderListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Adapter to back the ListView with a list of bookmarks.
-     */
-    private class BookmarksListAdapter extends CursorAdapter {
-        private static final int VIEW_TYPE_ITEM = 0;
-        private static final int VIEW_TYPE_FOLDER = 1;
-
-        private static final int VIEW_TYPE_COUNT = 2;
-
-        // mParentStack holds folder id/title pairs that allow us to navigate
-        // back up the folder heirarchy.
-        private LinkedList<Pair<Integer, String>> mParentStack;
-
-        public BookmarksListAdapter(Context context, Cursor cursor) {
-            // Initializing with a null cursor.
-            super(context, cursor);
-
-            mParentStack = new LinkedList<Pair<Integer, String>>();
-
-            // Add the root folder to the stack
-            Pair<Integer, String> rootFolder = new Pair<Integer, String>(Bookmarks.FIXED_ROOT_ID, "");
-            mParentStack.addFirst(rootFolder);
-        }
-
-        // Refresh the current folder by executing a new task.
-        private void refreshCurrentFolder() {
-            if (mListener != null) {
-                mListener.onRefreshFolder(mParentStack.peek().first);
-            }
-        }
-
-        /**
-         * Moves to parent folder, if one exists.
-         */
-        public void moveToParentFolder() {
-            // If we're already at the root, we can't move to a parent folder
-            if (mParentStack.size() != 1) {
-                mParentStack.removeFirst();
-                refreshCurrentFolder();
-            }
-        }
-
-        /**
-         * Moves to child folder, given a folderId.
-         *
-         * @param folderId The id of the folder to show.
-         * @param folderTitle The title of the folder to show.
-         */
-        public void moveToChildFolder(int folderId, String folderTitle) {
-            Pair<Integer, String> folderPair = new Pair<Integer, String>(folderId, folderTitle);
-            mParentStack.addFirst(folderPair);
-            refreshCurrentFolder();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public int getItemViewType(int position) {
-            // The position also reflects the opened child folder row.
-            if (isShowingChildFolder()) {
-                if (position == 0) {
-                    return VIEW_TYPE_FOLDER;
-                }
-
-                // Accounting for the folder view.
-                position--;
-            }
-
-            Cursor c = getCursor();
-
-            if (!c.moveToPosition(position)) {
-                throw new IllegalStateException("Couldn't move cursor to position " + position);
-            }
-
-            return getItemViewType(c);
-        }
-
-        /**
-         * Returns the type of the item at the given position in the cursor.
-         *
-         * @param cursor A cursor moved to the required position.
-         * @return The type of the item.
-         */
-        public int getItemViewType(Cursor cursor) {
-            if (cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER) {
-                return VIEW_TYPE_FOLDER;
-            }
-
-            // Default to returning normal item type.
-            return VIEW_TYPE_ITEM;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public int getViewTypeCount() {
-            return VIEW_TYPE_COUNT;
-        }
-
-        /**
-         * Get the title of the folder given a cursor moved to the position.
-         *
-         * @param cursor A cursor moved to the required position.
-         * @return The title of the folder at the position.
-         */
-        public String getFolderTitle(Cursor c) {
-            String guid = c.getString(c.getColumnIndexOrThrow(Bookmarks.GUID));
-
-            // If we don't have a special GUID, just return the folder title from the DB.
-            if (guid == null || guid.length() == 12) {
-                return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
-            }
-
-            // Use localized strings for special folder names.
-            if (guid.equals(Bookmarks.FAKE_DESKTOP_FOLDER_GUID)) {
-                return getResources().getString(R.string.bookmarks_folder_desktop);
-            } else if (guid.equals(Bookmarks.MENU_FOLDER_GUID)) {
-                return getResources().getString(R.string.bookmarks_folder_menu);
-            } else if (guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID)) {
-                return getResources().getString(R.string.bookmarks_folder_toolbar);
-            } else if (guid.equals(Bookmarks.UNFILED_FOLDER_GUID)) {
-                return getResources().getString(R.string.bookmarks_folder_unfiled);
-            }
-
-            // If for some reason we have a folder with a special GUID, but it's not one of
-            // the special folders we expect in the UI, just return the title from the DB.
-            return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
-        }
-
-        /**
-         * @return true, if currently showing a child folder, false otherwise.
-         */
-        public boolean isShowingChildFolder() {
-            return (mParentStack.peek().first != Bookmarks.FIXED_ROOT_ID);
-        }
-
-        @Override
-        public int getCount() {
-            return super.getCount() + (isShowingChildFolder() ? 1 : 0);
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            // The position also reflects the opened child folder row.
-            if (isShowingChildFolder()) {
-                if (position == 0) {
-                    BookmarkFolderView folder = (BookmarkFolderView) LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_folder_row, null);
-                    folder.setText(mParentStack.peek().second);
-                    folder.open();
-                    return folder;
-                }
-
-                // Accounting for the folder view.
-                position--;
-            }
-
-            return super.getView(position, convertView, parent);
-        }
-
-        @Override
-        public void bindView(View view, Context context, Cursor cursor) {
-            final int viewType = getItemViewType(cursor);
-
-            if (viewType == VIEW_TYPE_ITEM) {
-                TwoLinePageRow row = (TwoLinePageRow) view;
-                row.updateFromCursor(cursor);
-            } else {
-                BookmarkFolderView row = (BookmarkFolderView) view;
-                row.setText(getFolderTitle(cursor));
-                row.close();
-            }
-        }
-
-        @Override
-        public View newView(Context context, Cursor cursor, ViewGroup parent) {
-            final int viewType = getItemViewType(cursor);
-
-            final int resId;
-            if (viewType == VIEW_TYPE_ITEM) {
-                resId = R.layout.home_item_row;
-            } else {
-                resId = R.layout.bookmark_folder_row;
-            }
-
-            return LayoutInflater.from(parent.getContext()).inflate(resId, null);
-        }
-    }
 }
--- a/mobile/android/base/home/BookmarksPage.java
+++ b/mobile/android/base/home/BookmarksPage.java
@@ -3,20 +3,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.home.BookmarksListView.OnRefreshFolderListener;
+import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
-import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
 import android.view.LayoutInflater;
@@ -39,16 +38,19 @@ public class BookmarksPage extends HomeF
     private static final String BOOKMARKS_FOLDER_KEY = "folder_id";
 
     // List of bookmarks.
     private BookmarksListView mList;
 
     // Grid of top bookmarks.
     private TopBookmarksView mTopBookmarks;
 
+    // Adapter for list of bookmarks.
+    private BookmarksListAdapter mListAdapter;
+
     // Callback for loaders.
     private CursorLoaderCallbacks mLoaderCallbacks;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         BookmarksListView list = (BookmarksListView) inflater.inflate(R.layout.home_bookmarks_page, container, false);
 
         mTopBookmarks = new TopBookmarksView(getActivity());
@@ -66,47 +68,52 @@ public class BookmarksPage extends HomeF
             listener = (OnUrlOpenListener) getActivity();
         } catch (ClassCastException e) {
             throw new ClassCastException(getActivity().toString()
                     + " must implement HomePager.OnUrlOpenListener");
         }
 
         mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
         mList.setOnUrlOpenListener(listener);
-        mList.setOnRefreshFolderListener(new OnRefreshFolderListener() {
-            @Override
-            public void onRefreshFolder(int folderId) {
-                // Restart the loader with folder as the argument.
-                Bundle bundle = new Bundle();
-                bundle.putInt(BOOKMARKS_FOLDER_KEY, folderId);
-                getLoaderManager().restartLoader(BOOKMARKS_LIST_LOADER_ID, bundle, mLoaderCallbacks);
-            }
-        });
 
         registerForContextMenu(mList);
 
         mTopBookmarks.setOnUrlOpenListener(listener);
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        // Setup the list adapter.
+        mListAdapter = new BookmarksListAdapter(getActivity(), null);
+        mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() {
+            @Override
+            public void onRefreshFolder(int folderId) {
+                // Restart the loader with folder as the argument.
+                Bundle bundle = new Bundle();
+                bundle.putInt(BOOKMARKS_FOLDER_KEY, folderId);
+                getLoaderManager().restartLoader(BOOKMARKS_LIST_LOADER_ID, bundle, mLoaderCallbacks);
+            }
+        });
+        mList.setAdapter(mListAdapter);
+
         // Create callbacks before the initial loader is started.
         mLoaderCallbacks = new CursorLoaderCallbacks();
 
         // Reconnect to the loader only if present.
         final LoaderManager manager = getLoaderManager();
         manager.initLoader(BOOKMARKS_LIST_LOADER_ID, null, mLoaderCallbacks);
         manager.initLoader(TOP_BOOKMARKS_LOADER_ID, null, mLoaderCallbacks);
     }
 
     @Override
     public void onDestroyView() {
         mList = null;
+        mListAdapter = null;
         super.onDestroyView();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
         // Reattach the fragment, forcing a reinflation of its view.
@@ -184,34 +191,34 @@ public class BookmarksPage extends HomeF
             return null;
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
             final int loaderId = loader.getId();
             switch(loaderId) {
                 case BOOKMARKS_LIST_LOADER_ID: {
-                    mList.refreshFromCursor(c);
+                    mListAdapter.swapCursor(c);
                     break;
                 }
 
                 case TOP_BOOKMARKS_LOADER_ID: {
                     mTopBookmarks.refreshFromCursor(c);
                     break;
                 }
             }
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             final int loaderId = loader.getId();
             switch(loaderId) {
                 case BOOKMARKS_LIST_LOADER_ID: {
                     if (mList != null) {
-                        mList.refreshFromCursor(null);
+                        mListAdapter.swapCursor(null);
                     }
                     break;
                 }
 
                 case TOP_BOOKMARKS_LOADER_ID: {
                     if (mTopBookmarks != null) {
                         mTopBookmarks.refreshFromCursor(null);
                         break;