Bug 737867 - Apply ViewHolder pattern in AwesomeBar's All/Bookmarks tabs (r=mfinkle)
authorLucas Rocha <lucasr@mozilla.com>
Tue, 27 Mar 2012 18:05:41 +0100
changeset 92038 07b68ea1083f5931abd23cfc898831c9e703c43f
parent 92037 eda41c845ed599c3b2cdee48223061d7deafd1c0
child 92039 4c2e0ab2fa4f3c314e85e75829b53562b16db652
push idunknown
push userunknown
push dateunknown
reviewersmfinkle
bugs737867
milestone13.0a2
Bug 737867 - Apply ViewHolder pattern in AwesomeBar's All/Bookmarks tabs (r=mfinkle)
mobile/android/base/AwesomeBarTabs.java
--- a/mobile/android/base/AwesomeBarTabs.java
+++ b/mobile/android/base/AwesomeBarTabs.java
@@ -112,16 +112,22 @@ public class AwesomeBarTabs extends TabH
     // prefs key (just like XUL-based fennec)
     private static final int MAX_RESULTS = 100;
 
     public interface OnUrlOpenListener {
         public void onUrlOpen(String url);
         public void onSearch(String engine);
     }
 
+    private class ViewHolder {
+        public TextView titleView;
+        public TextView urlView;
+        public ImageView faviconView;
+    }
+
     private class HistoryListAdapter extends SimpleExpandableListAdapter {
         public HistoryListAdapter(Context context, List<? extends Map<String, ?>> groupData,
                 int groupLayout, String[] groupFrom, int[] groupTo,
                 List<? extends List<? extends Map<String, ?>>> childData,
                 int childLayout, String[] childFrom, int[] childTo) {
 
             super(context, groupData, groupLayout, groupFrom, groupTo,
                   childData, childLayout, childFrom, childTo);
@@ -147,73 +153,28 @@ public class AwesomeBarTabs extends TabH
                 Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
                 favicon.setImageBitmap(bitmap);
             }
 
             return childView;
         }
     }
 
-    private class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder {
-        private boolean updateFavicon(View view, Cursor cursor, int faviconIndex) {
-            byte[] b = cursor.getBlob(faviconIndex);
-            ImageView favicon = (ImageView) view;
-
-            if (b == null) {
-                favicon.setImageDrawable(null);
-            } else {
-                Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
-                favicon.setImageBitmap(bitmap);
-            }
-
-            return true;
-        }
-
-        private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
-            String title = cursor.getString(titleIndex);
-            TextView titleView = (TextView)view;
-            // Use the URL instead of an empty title for consistency with the normal URL
-            // bar view - this is the equivalent of getDisplayTitle() in Tab.java
-            if (TextUtils.isEmpty(title)) {
-                int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
-                title = cursor.getString(urlIndex);
-            }
-
-            titleView.setText(title);
-            return true;
-        }
-
-        public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
-            int faviconIndex = cursor.getColumnIndexOrThrow(URLColumns.FAVICON);
-            if (columnIndex == faviconIndex) {
-                return updateFavicon(view, cursor, faviconIndex);
-            }
-
-            int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
-            if (columnIndex == titleIndex) {
-                return updateTitle(view, cursor, titleIndex);
-            }
-
-            // Other columns are handled automatically
-            return false;
-        }
-    }
-
     private class BookmarksListAdapter extends SimpleCursorAdapter {
         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 LayoutInflater mInflater;
         private Resources mResources;
         private LinkedList<Pair<Integer, String>> mParentStack;
         private LinearLayout mBookmarksTitleView;
 
-        public BookmarksListAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
-            super(context, layout, c, from, to);
+        public BookmarksListAdapter(Context context, Cursor c) {
+            super(context, -1, c, new String[] {}, new int[] {});
 
             mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             mResources = mContext.getResources();
 
             // mParentStack holds folder id/title pairs that allow us to navigate
             // back up the folder heirarchy
             mParentStack = new LinkedList<Pair<Integer, String>>();
 
@@ -289,25 +250,48 @@ public class AwesomeBarTabs extends TabH
             // 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));
         }
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             int viewType = getItemViewType(position);
+            ViewHolder viewHolder = null;
 
-            if (viewType == VIEW_TYPE_ITEM)
-                return super.getView(position, convertView, parent);
+            if (convertView == null) {
+                if (viewType == VIEW_TYPE_ITEM)
+                    convertView = mInflater.inflate(R.layout.awesomebar_row, null);
+                else
+                    convertView = mInflater.inflate(R.layout.awesomebar_folder_row, null);
+
+                viewHolder = new ViewHolder();
+                viewHolder.titleView = (TextView) convertView.findViewById(R.id.title);
+                viewHolder.faviconView = (ImageView) convertView.findViewById(R.id.favicon);
+
+                if (viewType == VIEW_TYPE_ITEM)
+                    viewHolder.urlView = (TextView) convertView.findViewById(R.id.url);
 
-            if (convertView == null)
-                convertView = mInflater.inflate(R.layout.awesomebar_folder_row, null);
+                convertView.setTag(viewHolder);
+            } else {
+                viewHolder = (ViewHolder) convertView.getTag();
+            }
+
+            Cursor cursor = getCursor();
+            if (!cursor.moveToPosition(position))
+                throw new IllegalStateException("Couldn't move cursor to position " + position);
 
-            TextView titleView = (TextView) convertView.findViewById(R.id.title);
-            titleView.setText(getFolderTitle(position));
+            if (viewType == VIEW_TYPE_ITEM) {
+                updateTitle(viewHolder.titleView, cursor);
+                updateUrl(viewHolder.urlView, cursor);
+            } else {
+                viewHolder.titleView.setText(getFolderTitle(position));
+            }
+
+            updateFavicon(viewHolder.faviconView, cursor);
 
             return convertView;
         }
 
         public LinearLayout getHeaderView() {
             return mBookmarksTitleView;
         }
 
@@ -355,28 +339,17 @@ public class AwesomeBarTabs extends TabH
                     handleBookmarkItemClick(parent, view, position, id);
                 }
             });
             
             // We need to add the header before we set the adapter, hence make it null
             list.setAdapter(null);
 
             if (mBookmarksAdapter == null) {
-                // Load the list using a custom adapter so we can create the bitmaps
-                mBookmarksAdapter = new BookmarksListAdapter(
-                    mContext,
-                    R.layout.awesomebar_row,
-                    cursor,
-                    new String[] { URLColumns.TITLE,
-                                   URLColumns.URL,
-                                   URLColumns.FAVICON },
-                    new int[] { R.id.title, R.id.url, R.id.favicon }
-                );
-
-                mBookmarksAdapter.setViewBinder(new AwesomeCursorViewBinder());
+                mBookmarksAdapter = new BookmarksListAdapter(mContext, cursor);
             } else {
                 mBookmarksAdapter.changeCursor(cursor);
             }
 
             LinearLayout headerView = mBookmarksAdapter.getHeaderView();
             if (headerView == null) {
                 headerView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.awesomebar_header_row, null);
                 mBookmarksAdapter.setHeaderView(headerView);
@@ -590,21 +563,24 @@ public class AwesomeBarTabs extends TabH
 
             expandAllGroups(historyList);
 
             mHistoryQueryTask = null;
         }
     }
 
     private class AwesomeBarCursorAdapter extends SimpleCursorAdapter {
+        private LayoutInflater mInflater;
         private String mSearchTerm;
 
-        public AwesomeBarCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
-            super(context, layout, c, from, to);
+        public AwesomeBarCursorAdapter(Context context) {
+            super(context, -1, null, new String[] {}, new int[] {});
             mSearchTerm = "";
+
+            mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         }
 
         public void filter(String searchTerm) {
             mSearchTerm = searchTerm;
             getFilter().filter(searchTerm);
         }
 
         // Add the search engines to the number of reported results.
@@ -636,65 +612,78 @@ public class AwesomeBarTabs extends TabH
                 Log.e(LOGTAG, "error getting json arguments");
             }
 
             return engineName;
         }
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final int resultCount = super.getCount();
-            if (position < resultCount)
-                return super.getView(position, convertView, parent);
+            ViewHolder viewHolder = null;
+
+            if (convertView == null) {
+                convertView = mInflater.inflate(R.layout.awesomebar_row, null);
+
+                viewHolder = new ViewHolder();
+                viewHolder.titleView = (TextView) convertView.findViewById(R.id.title);
+                viewHolder.urlView = (TextView) convertView.findViewById(R.id.url);
+                viewHolder.faviconView = (ImageView) convertView.findViewById(R.id.favicon);
+
+                convertView.setTag(viewHolder);
+            } else {
+                viewHolder = (ViewHolder) convertView.getTag();
+            }
 
-            View v;
-            if (convertView == null)
-                v = newView(mContext, null, parent);
-            else
-                v = convertView;
-            bindSearchEngineView(position - resultCount, v);
+            final int resultCount = super.getCount();
+            if (position < resultCount) {
+                Cursor cursor = getCursor();
+                if (!cursor.moveToPosition(position))
+                    throw new IllegalStateException("Couldn't move cursor to position " + position);
 
-            return v;
+                updateTitle(viewHolder.titleView, cursor);
+                updateUrl(viewHolder.urlView, cursor);
+                updateFavicon(viewHolder.faviconView, cursor);
+            } else {
+                bindSearchEngineView(position - resultCount, viewHolder);
+            }
+
+            return convertView;
         }
 
         private Drawable getDrawableFromDataURI(String dataURI) {
             String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
             Drawable drawable = null;
             try {
                 byte[] bytes = GeckoAppShell.decodeBase64(base64, GeckoAppShell.BASE64_DEFAULT);
                 ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
                 drawable = Drawable.createFromStream(stream, "src");
                 stream.close();
             } catch (IllegalArgumentException e) {
                 Log.i(LOGTAG, "exception while decoding drawable: " + base64, e);
             } catch (IOException e) { }
             return drawable;
         }
 
-        private void bindSearchEngineView(int position, View view) {
+        private void bindSearchEngineView(int position, ViewHolder viewHolder) {
             String name;
             String iconURI;
             String searchText = getResources().getString(R.string.awesomebar_search_engine, mSearchTerm);
             try {
                 JSONObject searchEngine = mSearchEngines.getJSONObject(position);
                 name = searchEngine.getString("name");
                 iconURI = searchEngine.getString("iconURI");
             } catch (JSONException e) {
                 Log.e(LOGTAG, "error getting json arguments");
                 return;
             }
 
-            TextView titleView = (TextView) view.findViewById(R.id.title);
-            TextView urlView = (TextView) view.findViewById(R.id.url);
-            ImageView faviconView = (ImageView) view.findViewById(R.id.favicon);
-
-            titleView.setText(name);
-            urlView.setText(searchText);
+            viewHolder.titleView.setText(name);
+            viewHolder.urlView.setText(searchText);
             Drawable drawable = getDrawableFromDataURI(iconURI);
-            faviconView.setImageDrawable(drawable);
+            viewHolder.faviconView.setImageDrawable(drawable);
         }
     };
 
     public AwesomeBarTabs(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         Log.d(LOGTAG, "Creating AwesomeBarTabs");
 
@@ -788,27 +777,17 @@ public class AwesomeBarTabs extends TabH
     private void addAllPagesTab() {
         Log.d(LOGTAG, "Creating All Pages tab");
 
         addAwesomeTab(ALL_PAGES_TAB,
                       R.string.awesomebar_all_pages_title,
                       R.id.all_pages_list);
 
         // Load the list using a custom adapter so we can create the bitmaps
-        mAllPagesCursorAdapter = new AwesomeBarCursorAdapter(
-            mContext,
-            R.layout.awesomebar_row,
-            null,
-            new String[] { URLColumns.TITLE,
-                           URLColumns.URL,
-                           URLColumns.FAVICON },
-            new int[] { R.id.title, R.id.url, R.id.favicon }
-        );
-
-        mAllPagesCursorAdapter.setViewBinder(new AwesomeCursorViewBinder());
+        mAllPagesCursorAdapter = new AwesomeBarCursorAdapter(mContext);
 
         mAllPagesCursorAdapter.setFilterQueryProvider(new FilterQueryProvider() {
             public Cursor runQuery(CharSequence constraint) {
                 long start = SystemClock.uptimeMillis();
 
                 Cursor c = BrowserDB.filter(mContentResolver, constraint, MAX_RESULTS);
                 c.getCount(); // ensure the query runs at least once
 
@@ -917,16 +896,47 @@ public class AwesomeBarTabs extends TabH
             if (mUrlOpenListener != null)
                 mUrlOpenListener.onUrlOpen(url);
         } else {
             if (mUrlOpenListener != null)
                 mUrlOpenListener.onSearch((String)item);
         }
     }
 
+    private void updateFavicon(ImageView faviconView, Cursor cursor) {
+        byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON));
+        if (b == null) {
+            faviconView.setImageDrawable(null);
+        } else {
+            Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
+            faviconView.setImageBitmap(bitmap);
+        }
+    }
+
+    private void updateTitle(TextView titleView, Cursor cursor) {
+        int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
+        String title = cursor.getString(titleIndex);
+
+        // Use the URL instead of an empty title for consistency with the normal URL
+        // bar view - this is the equivalent of getDisplayTitle() in Tab.java
+        if (TextUtils.isEmpty(title)) {
+            int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
+            title = cursor.getString(urlIndex);
+        }
+
+        titleView.setText(title);
+    }
+
+    private void updateUrl(TextView urlView, Cursor cursor) {
+        int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
+        String url = cursor.getString(urlIndex);
+
+        urlView.setText(url);
+    }
+
     public void setOnUrlOpenListener(OnUrlOpenListener listener) {
         mUrlOpenListener = listener;
     }
 
     public void destroy() {
         Cursor allPagesCursor = mAllPagesCursorAdapter.getCursor();
         if (allPagesCursor != null)
             allPagesCursor.close();