Bug 889612: TopBookmarksAdapter should be controlled by the BookmarksPage fragment. [r=bnicholson]
authorSriram Ramasubramanian <sriram@mozilla.com>
Tue, 02 Jul 2013 14:31:34 -0700
changeset 143376 d9832319249ba5de8f92f85160899f65bffbc243
parent 143375 22104d00bbdc131d564ae76165306d0f4bc2465d
child 143377 c4436a3f0c730bce3c54f39db46c1d107771da91
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: TopBookmarksAdapter should be controlled by the BookmarksPage fragment. [r=bnicholson]
mobile/android/base/Makefile.in
mobile/android/base/home/BookmarksListView.java
mobile/android/base/home/BookmarksPage.java
mobile/android/base/home/TopBookmarksAdapter.java
mobile/android/base/home/TopBookmarksView.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -220,16 +220,17 @@ FENNEC_JAVA_FILES = \
   home/HomePagerTabStrip.java \
   home/FadedTextView.java \
   home/FaviconsLoader.java \
   home/SearchEngine.java \
   home/SearchEngineRow.java \
   home/SimpleCursorLoader.java \
   home/SuggestClient.java \
   home/TopBookmarkItemView.java \
+  home/TopBookmarksAdapter.java \
   home/TopBookmarksView.java \
   home/TwoLinePageRow.java \
   home/VisitedPage.java \
   menu/GeckoMenu.java \
   menu/GeckoMenuInflater.java \
   menu/GeckoMenuItem.java \
   menu/GeckoSubMenu.java \
   menu/MenuItemActionBar.java \
--- a/mobile/android/base/home/BookmarksListView.java
+++ b/mobile/android/base/home/BookmarksListView.java
@@ -19,17 +19,16 @@ import android.view.ViewConfiguration;
 import android.widget.AdapterView;
 import android.widget.ListView;
 
 /**
  * A ListView of bookmarks.
  */
 public class BookmarksListView extends HomeListView
                                implements AdapterView.OnItemClickListener{
-    
     public static final String LOGTAG = "GeckoBookmarksListView";
 
     // The last motion event that was intercepted.
     private MotionEvent mMotionEvent;
 
     // The default touch slop.
     private int mTouchSlop;
 
--- a/mobile/android/base/home/BookmarksPage.java
+++ b/mobile/android/base/home/BookmarksPage.java
@@ -1,32 +1,46 @@
 /* -*- 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.Favicons;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.BrowserContract.Thumbnails;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.db.BrowserDB.URLColumns;
+import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.home.TopBookmarksView.Thumbnail;
+import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.util.UiAsyncTask;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
+import android.graphics.Bitmap;
 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;
 import android.view.View;
 import android.view.ViewGroup;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * A page in about:home that displays a ListView of bookmarks.
  */
 public class BookmarksPage extends HomeFragment {
     public static final String LOGTAG = "GeckoBookmarksPage";
 
     // Cursor loader ID for list of bookmarks.
     private static final int BOOKMARKS_LIST_LOADER_ID = 0;
@@ -41,16 +55,19 @@ public class BookmarksPage extends HomeF
     private BookmarksListView mList;
 
     // Grid of top bookmarks.
     private TopBookmarksView mTopBookmarks;
 
     // Adapter for list of bookmarks.
     private BookmarksListAdapter mListAdapter;
 
+    // Adapter for grid of bookmarks.
+    private TopBookmarksAdapter mTopBookmarksAdapter;
+
     // 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());
@@ -78,16 +95,20 @@ public class BookmarksPage extends HomeF
 
         mTopBookmarks.setOnUrlOpenListener(listener);
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        // Setup the top bookmarks adapter.
+        mTopBookmarksAdapter = new TopBookmarksAdapter(getActivity(), null);
+        mTopBookmarks.setAdapter(mTopBookmarksAdapter);
+
         // 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);
@@ -104,16 +125,18 @@ public class BookmarksPage extends HomeF
         manager.initLoader(BOOKMARKS_LIST_LOADER_ID, null, mLoaderCallbacks);
         manager.initLoader(TOP_BOOKMARKS_LOADER_ID, null, mLoaderCallbacks);
     }
 
     @Override
     public void onDestroyView() {
         mList = null;
         mListAdapter = null;
+        mTopBookmarks = null;
+        mTopBookmarksAdapter = null;
         super.onDestroyView();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
         // Reattach the fragment, forcing a reinflation of its view.
@@ -196,17 +219,22 @@ public class BookmarksPage extends HomeF
             final int loaderId = loader.getId();
             switch(loaderId) {
                 case BOOKMARKS_LIST_LOADER_ID: {
                     mListAdapter.swapCursor(c);
                     break;
                 }
 
                 case TOP_BOOKMARKS_LOADER_ID: {
-                    mTopBookmarks.refreshFromCursor(c);
+                    mTopBookmarksAdapter.swapCursor(c);
+
+                    // Load the thumbnails.
+                    if (c.getCount() > 0) {
+                        new LoadThumbnailsTask(getActivity()).execute(c);
+                    }
                     break;
                 }
             }
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             final int loaderId = loader.getId();
@@ -215,16 +243,91 @@ public class BookmarksPage extends HomeF
                     if (mList != null) {
                         mListAdapter.swapCursor(null);
                     }
                     break;
                 }
 
                 case TOP_BOOKMARKS_LOADER_ID: {
                     if (mTopBookmarks != null) {
-                        mTopBookmarks.refreshFromCursor(null);
+                        mTopBookmarksAdapter.swapCursor(null);
                         break;
                     }
                 }
             }
         }
     }
+
+    /**
+     * An AsyncTask to load the thumbnails from a cursor.
+     */
+    private class LoadThumbnailsTask extends UiAsyncTask<Cursor, Void, Map<String, Thumbnail>> {
+        private final Context mContext;
+
+        public LoadThumbnailsTask(Context context) {
+            super(ThreadUtils.getBackgroundHandler());
+            mContext = context;
+        }
+
+        @Override
+        protected Map<String, Thumbnail> doInBackground(Cursor... params) {
+            // TopBookmarksAdapter's cursor.
+            final Cursor adapterCursor = params[0];
+            if (adapterCursor == null || !adapterCursor.moveToFirst()) {
+                return null;
+            }
+
+            final List<String> urls = new ArrayList<String>();
+            do {
+                final String url = adapterCursor.getString(adapterCursor.getColumnIndexOrThrow(URLColumns.URL));
+                urls.add(url);
+            } while (adapterCursor.moveToNext());
+
+            if (urls.size() == 0) {
+                return null;
+            }
+
+            final Map<String, Thumbnail> thumbnails = new HashMap<String, Thumbnail>();
+
+            // Query the DB for thumbnails.
+            final ContentResolver cr = mContext.getContentResolver();
+            final Cursor cursor = BrowserDB.getThumbnailsForUrls(cr, urls);
+
+            try {
+                if (cursor != null && cursor.moveToFirst()) {
+                    do {
+                        // Try to get the thumbnail, if cursor is valid.
+                        String url = cursor.getString(cursor.getColumnIndexOrThrow(Thumbnails.URL));
+                        final byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(Thumbnails.DATA));
+                        final Bitmap bitmap = (b == null ? null : BitmapUtils.decodeByteArray(b));
+
+                        if (bitmap != null) {
+                            thumbnails.put(url, new Thumbnail(bitmap, true));
+                        }
+                    } while (cursor.moveToNext());
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+
+            // Query the DB for favicons for the urls without thumbnails.
+            for (String url : urls) {
+                if (!thumbnails.containsKey(url)) {
+                    final Bitmap bitmap = BrowserDB.getFaviconForUrl(cr, url);
+                    if (bitmap != null) {
+                        // Favicons.scaleImage can return several different size favicons,
+                        // but will at least prevent this from being too large.
+                        thumbnails.put(url, new Thumbnail(Favicons.getInstance().scaleImage(bitmap), false));
+                    }
+                }
+            }
+
+            return thumbnails;
+        }
+
+        @Override
+        public void onPostExecute(Map<String, Thumbnail> thumbnails) {
+            mTopBookmarks.updateThumbnails(thumbnails);
+        }
+    }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/home/TopBookmarksAdapter.java
@@ -0,0 +1,64 @@
+/* -*- 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.db.BrowserDB.TopSitesCursorWrapper;
+import org.mozilla.gecko.db.BrowserDB.URLColumns;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter holding the pinned and top bookmarks.
+ */
+public class TopBookmarksAdapter extends CursorAdapter {
+    public TopBookmarksAdapter(Context context, Cursor cursor) {
+        super(context, cursor);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onContentChanged () {
+        // Don't do anything. We don't want to regenerate every time
+        // our database is updated.
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void bindView(View bindView, Context context, Cursor cursor) {
+        String url = "";
+        String title = "";
+        boolean pinned = false;
+
+        // Cursor is already moved to required position.
+        if (!cursor.isAfterLast()) {
+            url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
+            title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
+            pinned = ((TopSitesCursorWrapper) cursor).isPinned();
+        }
+
+        TopBookmarkItemView view = (TopBookmarkItemView) bindView;
+        view.setTitle(title);
+        view.setUrl(url);
+        view.setPinned(pinned);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View newView(Context context, Cursor cursor, ViewGroup parent) {
+        return new TopBookmarkItemView(context);
+    }
+}
\ No newline at end of file
--- a/mobile/android/base/home/TopBookmarksView.java
+++ b/mobile/android/base/home/TopBookmarksView.java
@@ -1,71 +1,53 @@
 /* -*- 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.Favicons;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ThumbnailHelper;
-import org.mozilla.gecko.db.BrowserContract.Thumbnails;
-import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
-import org.mozilla.gecko.db.BrowserDB.URLColumns;
-import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.UiAsyncTask;
 
-import android.content.ContentResolver;
 import android.content.Context;
-import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.support.v4.widget.CursorAdapter;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.GridView;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 /**
  * A grid view of top bookmarks and pinned tabs.
  * Each cell in the grid is a TopBookmarkItemView.
  */
 public class TopBookmarksView extends GridView {
     private static final String LOGTAG = "GeckoTopBookmarksView";
 
     // Max number of bookmarks that needs to be shown.
-    private int mMaxBookmarks;
+    private final int mMaxBookmarks;
 
     // Number of columns to show.
-    private int mNumColumns;
+    private final int mNumColumns;
 
     // On URL open listener.
     private OnUrlOpenListener mUrlOpenListener;
 
-    // A cursor based adapter backing this view.
-    protected TopBookmarksAdapter mAdapter;
-
     // Temporary cache to store the thumbnails until the next layout pass.
     private Map<String, Thumbnail> mThumbnailsCache;
 
     /**
      *  Class to hold the bitmap of cached thumbnails/favicons.
      */
-    private class Thumbnail {
+    public static class Thumbnail {
         // Thumbnail or favicon.
         private final boolean isThumbnail;
 
         // Bitmap of thumbnail/favicon.
         private final Bitmap bitmap;
 
         public Thumbnail(Bitmap bitmap, boolean isThumbnail) {
             this.bitmap = bitmap;
@@ -90,20 +72,16 @@ public class TopBookmarksView extends Gr
 
     /**
      * {@inheritDoc}
      */
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Initialize the adapter.
-        mAdapter = new TopBookmarksAdapter(getContext(), null);
-        setAdapter(mAdapter);
-
         setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 TopBookmarkItemView row = (TopBookmarkItemView) view;
                 String url = row.getUrl();
 
                 if (mUrlOpenListener != null && !TextUtils.isEmpty(url)) {
                     mUrlOpenListener.onUrlOpen(url);
@@ -111,25 +89,16 @@ public class TopBookmarksView extends Gr
             }
         });
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mAdapter = null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public int getColumnWidth() {
         // This method will be called from onMeasure() too.
         // It's better to use getMeasuredWidth(), as it is safe in this case.
         return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / mNumColumns;
     }
 
     /**
      * {@inheritDoc}
@@ -185,57 +154,46 @@ public class TopBookmarksView extends Gr
 
     /**
      * {@inheritDoc}
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
-        // If there are thumnails in the cache, update them.
+        // If there are thumbnails in the cache, update them.
         if (mThumbnailsCache != null) {
             updateThumbnails(mThumbnailsCache);
             mThumbnailsCache = null;
         }
     }
 
     /**
      * Set an url open listener to be used by this view.
      *
      * @param listener An url open listener for this view.
      */
     public void setOnUrlOpenListener(OnUrlOpenListener listener) {
         mUrlOpenListener = listener;
     }
 
     /**
-     * Refreshes the grid with the given cursor.
-     *
-     * @param cursor The cursor to use with the adapter.
-     */
-    public void refreshFromCursor(Cursor cursor) {
-        if (mAdapter == null) {
-            return;
-        }
-
-        mAdapter.swapCursor(cursor);
-
-        // Load the thumbnails.
-        if (mAdapter.getCount() > 0) {
-            new LoadThumbnailsTask().execute(cursor);
-        }
-    }
-
-    /**
      * Update the thumbnails returned by the db.
      *
      * @param thumbnails A map of urls and their thumbnail bitmaps.
      */
-    private void updateThumbnails(Map<String, Thumbnail> thumbnails) {
-        final int count = mAdapter.getCount();
+    public void updateThumbnails(Map<String, Thumbnail> thumbnails) {
+        // If there's a layout scheduled on this view, wait for it to happen
+        // by storing the thumbnails in a cache. If not, update them right away.
+        if (isLayoutRequested()) {
+            mThumbnailsCache = thumbnails;
+            return;
+        }
+
+        final int count = getAdapter().getCount();
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
 
             // The grid view might get temporarily out of sync with the
             // adapter refreshes (e.g. on device rotation).
             if (child == null) {
                 continue;
             }
@@ -254,144 +212,9 @@ public class TopBookmarksView extends Gr
                 } else if (thumbnail.isThumbnail) {
                     view.displayThumbnail(thumbnail.bitmap);
                 } else {
                     view.displayFavicon(thumbnail.bitmap);
                 }
             }
         }
     }
-
-    /**
-     * A cursor adapter holding the pinned and top bookmarks.
-     */
-    public class TopBookmarksAdapter extends CursorAdapter {
-        public TopBookmarksAdapter(Context context, Cursor cursor) {
-            super(context, cursor);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public int getCount() {
-            return Math.min(super.getCount(), mMaxBookmarks);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void onContentChanged () {
-            // Don't do anything. We don't want to regenerate every time
-            // our database is updated.
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void bindView(View bindView, Context context, Cursor cursor) {
-            String url = "";
-            String title = "";
-            boolean pinned = false;
-
-            // Cursor is already moved to required position.
-            if (!cursor.isAfterLast()) {
-                url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
-                title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
-                pinned = ((TopSitesCursorWrapper) cursor).isPinned();
-            }
-
-            TopBookmarkItemView view = (TopBookmarkItemView) bindView;
-            view.setTitle(title);
-            view.setUrl(url);
-            view.setPinned(pinned);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public View newView(Context context, Cursor cursor, ViewGroup parent) {
-            return new TopBookmarkItemView(context);
-        }
-    }
-
-    /**
-     * An AsyncTask to load the thumbnails from a cursor.
-     */
-    private class LoadThumbnailsTask extends UiAsyncTask<Cursor, Void, Map<String, Thumbnail>> {
-        public LoadThumbnailsTask() {
-            super(ThreadUtils.getBackgroundHandler());
-        }
-
-        @Override
-        protected Map<String, Thumbnail> doInBackground(Cursor... params) {
-            // TopBookmarksAdapter's cursor.
-            final Cursor adapterCursor = params[0];
-            if (adapterCursor == null || !adapterCursor.moveToFirst()) {
-                return null;
-            }
-
-            final List<String> urls = new ArrayList<String>();
-            do {
-                final String url = adapterCursor.getString(adapterCursor.getColumnIndexOrThrow(URLColumns.URL));
-                urls.add(url);
-            } while(adapterCursor.moveToNext());
-
-            if (urls.size() == 0) {
-                return null;
-            }
-
-            final Map<String, Thumbnail> thumbnails = new HashMap<String, Thumbnail>();
-
-            // Query the DB for thumbnails.
-            final ContentResolver cr = getContext().getContentResolver();
-            final Cursor cursor = BrowserDB.getThumbnailsForUrls(cr, urls);
-
-            try {
-                if (cursor != null && cursor.moveToFirst()) {
-                    do {
-                        // Try to get the thumbnail, if cursor is valid.
-                        String url = cursor.getString(cursor.getColumnIndexOrThrow(Thumbnails.URL));
-                        final byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(Thumbnails.DATA));
-                        final Bitmap bitmap = (b == null ? null : BitmapUtils.decodeByteArray(b));
-
-                        if (bitmap != null) {
-                            thumbnails.put(url, new Thumbnail(bitmap, true));
-                        }
-                    } while (cursor.moveToNext());
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-
-            // Query the DB for favicons for the urls without thumbnails.
-            for (String url : urls) {
-                if (!thumbnails.containsKey(url)) {
-                    final Bitmap bitmap = BrowserDB.getFaviconForUrl(cr, url);
-                    if (bitmap != null) {
-                        // Favicons.scaleImage can return several different size favicons,
-                        // but will at least prevent this from being too large.
-                        thumbnails.put(url, new Thumbnail(Favicons.getInstance().scaleImage(bitmap), false));
-                    }
-                }
-            }
-
-            return thumbnails;
-        }
-
-        @Override
-        public void onPostExecute(Map<String, Thumbnail> thumbnails) {
-            // If there's a layout scheduled on this view, wait for it to happen
-            // by storing the thumbnails in a cache. If not, update them right away.
-            if (isLayoutRequested()) {
-                mThumbnailsCache = thumbnails;
-            } else {
-                updateThumbnails(thumbnails);
-            }
-        }
-    }
 }