Bug 902038: MultiTypeCursorAdapter for history, bookmarks and browser search adapters. [r=lucasr]
authorSriram Ramasubramanian <sriram@mozilla.com>
Wed, 07 Aug 2013 08:50:52 -0700
changeset 143507 04d3d68298d01ed8213df96150a281179737cef1
parent 143506 5ac4c656ec55e074cd16081ec071d8f3749165de
child 143508 f7264772c802b90a599ded28248e0682d5a58300
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)
reviewerslucasr
bugs902038
milestone25.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 902038: MultiTypeCursorAdapter for history, bookmarks and browser search adapters. [r=lucasr]
mobile/android/base/home/BookmarksListAdapter.java
mobile/android/base/home/BrowserSearch.java
mobile/android/base/home/MostRecentPage.java
--- a/mobile/android/base/home/BookmarksListAdapter.java
+++ b/mobile/android/base/home/BookmarksListAdapter.java
@@ -6,32 +6,30 @@
 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 {
+class BookmarksListAdapter extends MultiTypeCursorAdapter {
     private static final int VIEW_TYPE_ITEM = 0;
     private static final int VIEW_TYPE_FOLDER = 1;
 
-    private static final int VIEW_TYPE_COUNT = 2;
+    private static final int[] VIEW_TYPES = new int[] { VIEW_TYPE_ITEM, VIEW_TYPE_FOLDER };
+    private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.bookmark_folder_row };
 
     // 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);
     }
 
@@ -39,17 +37,17 @@ class BookmarksListAdapter extends Curso
     // 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);
+        super(context, cursor, VIEW_TYPES, LAYOUT_TYPES);
 
         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);
     }
 
@@ -99,45 +97,25 @@ class BookmarksListAdapter extends Curso
             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) {
+        final Cursor c = getCursor(position);
+        if (c.getInt(c.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) {
@@ -174,53 +152,39 @@ class BookmarksListAdapter extends Curso
     }
 
     @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.
+    public void bindView(View view, Context context, int position) {
+        final int viewType = getItemViewType(position);
+
+        final Cursor cursor;
         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;
+                cursor = null;
+            } else {
+                // Accounting for the folder view.
+                position--;
+                cursor = getCursor(position);
             }
-
-            // Accounting for the folder view.
-            position--;
+        } else {
+            cursor = getCursor(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;
+            final TwoLinePageRow row = (TwoLinePageRow) view;
             row.updateFromCursor(cursor);
         } else {
-            BookmarkFolderView row = (BookmarkFolderView) view;
-            row.setText(getFolderTitle(context, cursor));
-            row.close();
+            final BookmarkFolderView row = (BookmarkFolderView) view;
+            if (cursor == null) {
+                row.setText(mParentStack.peek().second);
+                row.open();
+            } else {
+                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/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -29,17 +29,16 @@ import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.Loader;
-import android.support.v4.widget.SimpleCursorAdapter;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.WindowManager.LayoutParams;
@@ -224,22 +223,17 @@ public class BrowserSearch extends HomeF
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 // Account for the search engines
                 position -= getSuggestEngineCount();
-
-                final Cursor c = mAdapter.getCursor();
-                if (c == null || !c.moveToPosition(position)) {
-                    return;
-                }
-
+                final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
                 mUrlOpenListener.onUrlOpen(url);
             }
         });
 
         final ListSelectionListener listener = new ListSelectionListener();
         mList.setOnItemSelectedListener(listener);
         mList.setOnFocusChangeListener(listener);
@@ -625,25 +619,28 @@ public class BrowserSearch extends HomeF
         protected void onReset() {
             super.onReset();
 
             onStopLoading();
             mSuggestions = null;
         }
     }
 
-    private class SearchAdapter extends SimpleCursorAdapter {
+    private class SearchAdapter extends MultiTypeCursorAdapter {
         private static final int ROW_SEARCH = 0;
         private static final int ROW_STANDARD = 1;
         private static final int ROW_SUGGEST = 2;
 
-        private static final int ROW_TYPE_COUNT = 3;
-
         public SearchAdapter(Context context) {
-            super(context, -1, null, new String[] {}, new int[] {});
+            super(context, null, new int[] { ROW_STANDARD,
+                                             ROW_SEARCH,
+                                             ROW_SUGGEST },
+                                 new int[] { R.layout.home_item_row,
+                                             R.layout.home_search_item_row,
+                                             R.layout.home_search_item_row });
         }
 
         @Override
         public int getItemViewType(int position) {
             final int engine = getEngineIndex(position);
 
             if (engine == -1) {
                 return ROW_STANDARD;
@@ -654,23 +651,16 @@ public class BrowserSearch extends HomeF
                 // (bug 815937).
                 return ROW_SUGGEST;
             }
 
             return ROW_SEARCH;
         }
 
         @Override
-        public int getViewTypeCount() {
-            // view can be either a standard awesomebar row, a search engine
-            // row, or a suggestion row
-            return ROW_TYPE_COUNT;
-        }
-
-        @Override
         public boolean isEnabled(int position) {
             // If we're using a gamepad or keyboard, allow the row to be
             // focused so it can pass the focus to its child suggestion views.
             if (!mList.isInTouchMode()) {
                 return true;
             }
 
             // If the suggestion row only contains one item (the user-entered
@@ -694,60 +684,40 @@ public class BrowserSearch extends HomeF
             if (TextUtils.isEmpty(mSearchTerm)) {
                 return resultCount;
             }
 
             return resultCount + mSearchEngines.size();
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
+        public void bindView(View view, Context context, int position) {
             final int type = getItemViewType(position);
 
             if (type == ROW_SEARCH || type == ROW_SUGGEST) {
-                final SearchEngineRow row;
-                if (convertView == null) {
-                    row = (SearchEngineRow) mInflater.inflate(R.layout.home_search_item_row, mList, false);
-                    row.setOnUrlOpenListener(mUrlOpenListener);
-                    row.setOnSearchListener(mSearchListener);
-                    row.setOnEditSuggestionListener(mEditSuggestionListener);
-                } else {
-                    row = (SearchEngineRow) convertView;
-                }
-
+                final SearchEngineRow row = (SearchEngineRow) view;
+                row.setOnUrlOpenListener(mUrlOpenListener);
+                row.setOnSearchListener(mSearchListener);
+                row.setOnEditSuggestionListener(mEditSuggestionListener);
                 row.setSearchTerm(mSearchTerm);
 
                 final SearchEngine engine = mSearchEngines.get(getEngineIndex(position));
                 final boolean animate = (mAnimateSuggestions && engine.suggestions.size() > 0);
                 row.updateFromSearchEngine(engine, animate);
                 if (animate) {
                     // Only animate suggestions the first time they are shown
                     mAnimateSuggestions = false;
                 }
-
-                return row;
             } else {
-                final TwoLinePageRow row;
-                if (convertView == null) {
-                    row = (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, mList, false);
-                } else {
-                    row = (TwoLinePageRow) convertView;
-                }
-
                 // Account for the search engines
                 position -= getSuggestEngineCount();
 
-                final Cursor c = getCursor();
-                if (!c.moveToPosition(position)) {
-                    throw new IllegalStateException("Couldn't move cursor to position " + position);
-                }
-
+                final Cursor c = getCursor(position);
+                final TwoLinePageRow row = (TwoLinePageRow) view;
                 row.updateFromCursor(c);
-
-                return row;
             }
         }
 
         private int getEngineIndex(int position) {
             final int resultCount = super.getCount();
             final int suggestEngineCount = getSuggestEngineCount();
 
             // Return suggest engine index
@@ -867,9 +837,9 @@ public class BrowserSearch extends HomeF
 
         private void deselectRow() {
             if (mSelectedEngineRow != null) {
                 mSelectedEngineRow.onDeselected();
                 mSelectedEngineRow = null;
             }
         }
     }
-}
\ No newline at end of file
+}
--- a/mobile/android/base/home/MostRecentPage.java
+++ b/mobile/android/base/home/MostRecentPage.java
@@ -13,21 +13,20 @@ import org.mozilla.gecko.home.TwoLinePag
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
-import android.support.v4.widget.SimpleCursorAdapter;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
 
 import java.util.Date;
 
 /**
@@ -88,22 +87,17 @@ public class MostRecentPage extends Home
         final TextView title = (TextView) view.findViewById(R.id.title);
         title.setText(R.string.home_most_recent_title);
 
         mList = (ListView) view.findViewById(R.id.list);
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 position -= mAdapter.getMostRecentSectionsCountBefore(position);
-
-                final Cursor c = mAdapter.getCursor();
-                if (c == null || !c.moveToPosition(position)) {
-                    return;
-                }
-
+                final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
                 mUrlOpenListener.onUrlOpen(url);
             }
         });
 
         // Set empty page view.
         final View emptyView = view.findViewById(R.id.home_empty_view);
         ((ImageView) emptyView.findViewById(R.id.home_empty_image)).setImageResource(R.drawable.icon_most_recent_empty);
@@ -149,21 +143,22 @@ public class MostRecentPage extends Home
 
         @Override
         public Cursor loadCursor() {
             final ContentResolver cr = getContext().getContentResolver();
             return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT);
         }
     }
 
-    private static class MostRecentAdapter extends SimpleCursorAdapter {
+    private static class MostRecentAdapter extends MultiTypeCursorAdapter {
         private static final int ROW_HEADER = 0;
         private static final int ROW_STANDARD = 1;
 
-        private static final int ROW_TYPE_COUNT = 2;
+        private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
+        private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
 
         // For the time sections in history
         private static final long MS_PER_DAY = 86400000;
         private static final long MS_PER_WEEK = MS_PER_DAY * 7;
 
         // The time ranges for each section
         private static enum MostRecentSection {
             TODAY,
@@ -173,17 +168,18 @@ public class MostRecentPage extends Home
         };
 
         private final Context mContext;
 
         // Maps headers in the list with their respective sections
         private final SparseArray<MostRecentSection> mMostRecentSections;
 
         public MostRecentAdapter(Context context) {
-            super(context, -1, null, new String[] {}, new int[] {});
+            super(context, null, VIEW_TYPES, LAYOUT_TYPES);
+
             mContext = context;
 
             // Initialize map of history sections
             mMostRecentSections = new SparseArray<MostRecentSection>();
         }
 
         @Override
         public Object getItem(int position) {
@@ -202,22 +198,16 @@ public class MostRecentPage extends Home
             if (mMostRecentSections.get(position) != null) {
                 return ROW_HEADER;
             }
 
             return ROW_STANDARD;
         }
 
         @Override
-        public int getViewTypeCount() {
-            // view can be either a standard page row, or a header row
-            return ROW_TYPE_COUNT;
-        }
-
-        @Override
         public boolean isEnabled(int position) {
             return (getItemViewType(position) == ROW_STANDARD);
         }
 
         @Override
         public int getCount() {
             // Add the history section headers to the number of reported results.
             return super.getCount() + mMostRecentSections.size();
@@ -226,50 +216,29 @@ public class MostRecentPage extends Home
         @Override
         public Cursor swapCursor(Cursor cursor) {
             Cursor oldCursor = super.swapCursor(cursor);
             loadMostRecentSections(cursor);
             return oldCursor;
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
+        public void bindView(View view, Context context, int position) {
             final int type = getItemViewType(position);
 
             if (type == ROW_HEADER) {
-                final TextView row;
-                if (convertView == null) {
-                    row = (TextView) LayoutInflater.from(mContext).inflate(R.layout.home_header_row, parent, false);
-                } else {
-                    row = (TextView) convertView;
-                }
-
                 final MostRecentSection section = mMostRecentSections.get(position);
+                final TextView row = (TextView) view;
                 row.setText(getMostRecentSectionTitle(section));
-
-                return row;
             } else {
-                final TwoLinePageRow row;
-                if (convertView == null) {
-                    row = (TwoLinePageRow) LayoutInflater.from(mContext).inflate(R.layout.home_item_row, parent, false);
-                } else {
-                    row = (TwoLinePageRow) convertView;
-                }
-
-                // Account for the search engines
+                // Account for the most recent section headers
                 position -= getMostRecentSectionsCountBefore(position);
-
-                final Cursor c = getCursor();
-                if (!c.moveToPosition(position)) {
-                    throw new IllegalStateException("Couldn't move cursor to position " + position);
-                }
-
+                final Cursor c = getCursor(position);
+                final TwoLinePageRow row = (TwoLinePageRow) view;
                 row.updateFromCursor(c);
-
-                return row;
             }
         }
 
         private String getMostRecentSectionTitle(MostRecentSection section) {
             switch (section) {
             case TODAY:
                 return mContext.getString(R.string.history_today_section);
             case YESTERDAY: