author | Sriram Ramasubramanian <sriram@mozilla.com> |
Tue, 02 Jul 2013 14:31:34 -0700 | |
changeset 143376 | d9832319249ba5de8f92f85160899f65bffbc243 |
parent 143375 | 22104d00bbdc131d564ae76165306d0f4bc2465d |
child 143377 | c4436a3f0c730bce3c54f39db46c1d107771da91 |
push id | 25130 |
push user | lrocha@mozilla.com |
push date | Wed, 21 Aug 2013 09:41:27 +0000 |
treeherder | mozilla-central@b2486721572e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bnicholson |
bugs | 889612 |
milestone | 24.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
|
--- 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); - } - } - } }