| Index: chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java
|
| index 3e8dd59ae27124fd3e0dfd8c5a9c7f464658a144..50de3d912f0db768512f70706adeb6c894f6be3b 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java
|
| @@ -8,17 +8,19 @@ import android.appwidget.AppWidgetManager;
|
| import android.content.Context;
|
| import android.content.Intent;
|
| import android.content.SharedPreferences;
|
| +import android.content.res.Resources;
|
| import android.graphics.Bitmap;
|
| import android.net.Uri;
|
| import android.support.annotation.BinderThread;
|
| import android.support.annotation.UiThread;
|
| import android.text.TextUtils;
|
| -import android.util.Log;
|
| import android.widget.RemoteViews;
|
| import android.widget.RemoteViewsService;
|
|
|
| import com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider;
|
|
|
| +import org.chromium.base.ApiCompatibilityUtils;
|
| +import org.chromium.base.Log;
|
| import org.chromium.base.ThreadUtils;
|
| import org.chromium.base.annotations.SuppressFBWarnings;
|
| import org.chromium.base.library_loader.ProcessInitException;
|
| @@ -27,19 +29,21 @@ import org.chromium.chrome.R;
|
| import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
|
| import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
|
| import org.chromium.chrome.browser.bookmarks.BookmarkModel;
|
| -import org.chromium.chrome.browser.favicon.FaviconHelper;
|
| -import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
|
| +import org.chromium.chrome.browser.favicon.LargeIconBridge;
|
| +import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
|
| import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
|
| import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
|
| import org.chromium.chrome.browser.profiles.Profile;
|
| import org.chromium.chrome.browser.tab.Tab;
|
| import org.chromium.chrome.browser.util.IntentUtils;
|
| +import org.chromium.chrome.browser.widget.RoundedIconGenerator;
|
| import org.chromium.components.bookmarks.BookmarkId;
|
| import org.chromium.components.bookmarks.BookmarkType;
|
|
|
| import java.util.ArrayList;
|
| +import java.util.Collections;
|
| +import java.util.Comparator;
|
| import java.util.List;
|
| -import java.util.concurrent.Callable;
|
| import java.util.concurrent.LinkedBlockingQueue;
|
|
|
| import javax.annotation.Nullable;
|
| @@ -57,7 +61,7 @@ import javax.annotation.Nullable;
|
| */
|
| public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
|
|
| - private static final String TAG = "BookmarkThumbnailWidgetService";
|
| + private static final String TAG = "BookmarkWidget";
|
| private static final String ACTION_CHANGE_FOLDER_SUFFIX = ".CHANGE_FOLDER";
|
| private static final String PREF_CURRENT_FOLDER = "current_folder";
|
| private static final String EXTRA_FOLDER_ID = "folderId";
|
| @@ -139,47 +143,46 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| }
|
|
|
| /**
|
| - * Loads a BookmarkFolder synchronously on a binder thread.
|
| + * Called when the BookmarkLoader has finished loading the bookmark folder.
|
| */
|
| - private static class BookmarkLoader {
|
| - /** Used to transfer the result from the UI thread to the binder thread. */
|
| - private final LinkedBlockingQueue<BookmarkFolder> mResultQueue;
|
| + private interface BookmarkLoaderCallback {
|
| + @UiThread
|
| + void onBookmarksLoaded(BookmarkFolder folder);
|
| + }
|
|
|
| + /**
|
| + * Loads a BookmarkFolder asynchronously, and returns the result via BookmarkLoaderCallback.
|
| + *
|
| + * This class must be used only on the UI thread.
|
| + */
|
| + @UiThread
|
| + private static class BookmarkLoader {
|
| + private BookmarkLoaderCallback mCallback;
|
| private BookmarkFolder mFolder;
|
| private BookmarkModel mBookmarkModel;
|
| - private Profile mProfile;
|
| - private FaviconHelper mFaviconHelper;
|
| - private int mFaviconSizePx;
|
| + private LargeIconBridge mLargeIconBridge;
|
| + private RoundedIconGenerator mIconGenerator;
|
| + private int mMinIconSizeDp;
|
| + private int mDisplayedIconSize;
|
| + private int mCornerRadius;
|
| private int mRemainingTaskCount;
|
|
|
| - /**
|
| - * Loads the list of bookmarks is the given folder synchronously. This must not be called
|
| - * from the UI thread.
|
| - */
|
| - @BinderThread
|
| - public static BookmarkFolder loadBookmarksOnBinderThread(final Context context,
|
| - final BookmarkId folderId) {
|
| - BookmarkLoader loader = ThreadUtils.runOnUiThreadBlockingNoException(
|
| - new Callable<BookmarkLoader>() {
|
| - @Override
|
| - public BookmarkLoader call() {
|
| - return new BookmarkLoader(context, folderId);
|
| - }
|
| - });
|
| - try {
|
| - return loader.mResultQueue.take();
|
| - } catch (InterruptedException e) {
|
| - return null;
|
| - }
|
| - }
|
| + BookmarkLoader(Context context, final BookmarkId folderId,
|
| + BookmarkLoaderCallback callback) {
|
| + mCallback = callback;
|
| +
|
| + Resources res = context.getResources();
|
| + mLargeIconBridge = new LargeIconBridge(
|
| + Profile.getLastUsedProfile().getOriginalProfile());
|
| + mMinIconSizeDp = (int) res.getDimension(R.dimen.bookmark_item_min_icon_size);
|
| + mDisplayedIconSize = res.getDimensionPixelSize(R.dimen.bookmark_item_icon_size);
|
| + mCornerRadius = res.getDimensionPixelSize(R.dimen.bookmark_item_corner_radius);
|
| + int textSize = res.getDimensionPixelSize(R.dimen.bookmark_item_icon_text_size);
|
| + int iconColor = ApiCompatibilityUtils.getColor(res,
|
| + R.color.bookmark_icon_background_color);
|
| + mIconGenerator = new RoundedIconGenerator(mDisplayedIconSize, mDisplayedIconSize,
|
| + mCornerRadius, iconColor, textSize);
|
|
|
| - @UiThread
|
| - private BookmarkLoader(Context context, final BookmarkId folderId) {
|
| - mResultQueue = new LinkedBlockingQueue<>(1);
|
| - mProfile = Profile.getLastUsedProfile();
|
| - mFaviconHelper = new FaviconHelper();
|
| - mFaviconSizePx = context.getResources().getDimensionPixelSize(
|
| - R.dimen.default_favicon_size);
|
| mRemainingTaskCount = 1;
|
| mBookmarkModel = new BookmarkModel();
|
| mBookmarkModel.runAfterBookmarkModelLoaded(new Runnable() {
|
| @@ -190,7 +193,6 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| });
|
| }
|
|
|
| - @UiThread
|
| private void loadBookmarks(BookmarkId folderId) {
|
| mFolder = new BookmarkFolder();
|
|
|
| @@ -209,6 +211,15 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| mFolder.folder.parentId));
|
|
|
| List<BookmarkItem> items = mBookmarkModel.getBookmarksForFolder(folderId);
|
| +
|
| + // Move folders to the beginning of the list.
|
| + Collections.sort(items, new Comparator<BookmarkItem>() {
|
| + @Override
|
| + public int compare(BookmarkItem lhs, BookmarkItem rhs) {
|
| + return lhs.isFolder() == rhs.isFolder() ? 0 : lhs.isFolder() ? -1 : 1;
|
| + }
|
| + });
|
| +
|
| for (BookmarkItem item : items) {
|
| Bookmark bookmark = Bookmark.fromBookmarkItem(item);
|
| loadFavicon(bookmark);
|
| @@ -218,34 +229,38 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| taskFinished();
|
| }
|
|
|
| - @UiThread
|
| private void loadFavicon(final Bookmark bookmark) {
|
| - if (!bookmark.isFolder) {
|
| - mRemainingTaskCount++;
|
| - mFaviconHelper.getLocalFaviconImageForURL(mProfile, bookmark.url, mFaviconSizePx,
|
| - new FaviconImageCallback() {
|
| - @Override
|
| - public void onFaviconAvailable(Bitmap image, String iconUrl) {
|
| - bookmark.favicon = image;
|
| - taskFinished();
|
| - }
|
| - });
|
| - }
|
| + if (bookmark.isFolder) return;
|
| +
|
| + mRemainingTaskCount++;
|
| + LargeIconCallback callback = new LargeIconCallback() {
|
| + @Override
|
| + public void onLargeIconAvailable(Bitmap icon, int fallbackColor) {
|
| + if (icon == null) {
|
| + mIconGenerator.setBackgroundColor(fallbackColor);
|
| + icon = mIconGenerator.generateIconForUrl(bookmark.url);
|
| + } else {
|
| + icon = Bitmap.createScaledBitmap(icon, mDisplayedIconSize,
|
| + mDisplayedIconSize, true);
|
| + }
|
| + bookmark.favicon = icon;
|
| + taskFinished();
|
| + }
|
| + };
|
| + mLargeIconBridge.getLargeIconForUrl(bookmark.url, mMinIconSizeDp, callback);
|
| }
|
|
|
| - @UiThread
|
| private void taskFinished() {
|
| mRemainingTaskCount--;
|
| if (mRemainingTaskCount == 0) {
|
| - mResultQueue.add(mFolder);
|
| + mCallback.onBookmarksLoaded(mFolder);
|
| destroy();
|
| }
|
| }
|
|
|
| - @UiThread
|
| private void destroy() {
|
| mBookmarkModel.destroy();
|
| - mFaviconHelper.destroy();
|
| + mLargeIconBridge.destroy();
|
| }
|
| }
|
|
|
| @@ -357,7 +372,7 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| ? new BookmarkId(folderIdLong, BookmarkType.NORMAL)
|
| : null;
|
|
|
| - mCurrentFolder = BookmarkLoader.loadBookmarksOnBinderThread(mContext, folderId);
|
| + mCurrentFolder = loadBookmarks(folderId);
|
|
|
| mPreferences.edit()
|
| .putLong(PREF_CURRENT_FOLDER, mCurrentFolder != null
|
| @@ -367,6 +382,27 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| }
|
|
|
| @BinderThread
|
| + private BookmarkFolder loadBookmarks(final BookmarkId folderId) {
|
| + final LinkedBlockingQueue<BookmarkFolder> resultQueue = new LinkedBlockingQueue<>(1);
|
| + ThreadUtils.runOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + new BookmarkLoader(mContext, folderId, new BookmarkLoaderCallback() {
|
| + @Override
|
| + public void onBookmarksLoaded(BookmarkFolder folder) {
|
| + resultQueue.add(folder);
|
| + }
|
| + });
|
| + }
|
| + });
|
| + try {
|
| + return resultQueue.take();
|
| + } catch (InterruptedException e) {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + @BinderThread
|
| private Bookmark getBookmarkForPosition(int position) {
|
| if (mCurrentFolder == null) return null;
|
|
|
| @@ -421,7 +457,7 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
|
|
| Bookmark bookmark = getBookmarkForPosition(position);
|
| if (bookmark == null) {
|
| - Log.w(TAG, "Couldn't get bookmark for position " + position);
|
| + Log.w(TAG, "Couldn't get bookmark for position %d", position);
|
| return null;
|
| }
|
|
|
| @@ -431,33 +467,18 @@ public class BookmarkThumbnailWidgetService extends RemoteViewsService {
|
| ? mCurrentFolder.parent.id.getId()
|
| : bookmark.id.getId();
|
|
|
| - // Two layouts are needed because RemoteView does not supporting changing the scale type
|
| - // of an ImageView: boomarks crop their thumbnails, while folders stretch their icon.
|
| - RemoteViews views = bookmark.isFolder
|
| - ? new RemoteViews(mContext.getPackageName(),
|
| - R.layout.bookmark_thumbnail_widget_item_folder)
|
| - : new RemoteViews(mContext.getPackageName(),
|
| - R.layout.bookmark_thumbnail_widget_item);
|
| + RemoteViews views = new RemoteViews(mContext.getPackageName(),
|
| + R.layout.bookmark_thumbnail_widget_item);
|
|
|
| // Set the title of the bookmark. Use the url as a backup.
|
| - views.setTextViewText(R.id.label, TextUtils.isEmpty(title) ? url : title);
|
| + views.setTextViewText(R.id.title, TextUtils.isEmpty(title) ? url : title);
|
|
|
| - if (bookmark.isFolder) {
|
| - int thumbId = (bookmark == mCurrentFolder.folder)
|
| - ? R.drawable.thumb_bookmark_widget_folder_back_holo
|
| - : R.drawable.thumb_bookmark_widget_folder_holo;
|
| - views.setImageViewResource(R.id.thumb, thumbId);
|
| - views.setImageViewResource(R.id.favicon,
|
| - R.drawable.ic_bookmark_widget_bookmark_holo_dark);
|
| + if (bookmark == mCurrentFolder.folder) {
|
| + views.setImageViewResource(R.id.favicon, R.drawable.bookmark_back_normal);
|
| + } else if (bookmark.isFolder) {
|
| + views.setImageViewResource(R.id.favicon, R.drawable.bookmark_folder);
|
| } else {
|
| - if (bookmark.favicon != null) {
|
| - views.setImageViewBitmap(R.id.favicon, bookmark.favicon);
|
| - } else {
|
| - views.setImageViewResource(R.id.favicon, R.drawable.globe_favicon);
|
| - }
|
| -
|
| - // TODO(newt): update the view and get rid of the thumbnail, which is always empty.
|
| - views.setImageViewResource(R.id.thumb, R.drawable.browser_thumbnail);
|
| + views.setImageViewBitmap(R.id.favicon, bookmark.favicon);
|
| }
|
|
|
| Intent fillIn;
|
|
|