diff --git a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java index b0dcea4..f377df8 100644 --- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java @@ -6,6 +6,7 @@ package acr.browser.lightning.constant; import android.app.Activity; import android.app.Application; import android.graphics.Bitmap; +import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; @@ -28,6 +29,8 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; +import acr.browser.lightning.favicon.FaviconModel; +import acr.browser.lightning.favicon.FaviconUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; @@ -75,6 +78,7 @@ public final class BookmarkPage { private static final String END = ""; private static final String FOLDER_ICON = "folder.png"; + private static final String DEFAULT_ICON = "default.png"; @NonNull private static File getBookmarkPage(@NonNull Application application, @Nullable String folder) { @@ -87,8 +91,14 @@ public final class BookmarkPage { return new File(application.getCacheDir(), FOLDER_ICON); } + @NonNull + private static File getDefaultIconFile(@NonNull Application application) { + return new File(application.getCacheDir(), DEFAULT_ICON); + } + @Inject Application mApp; - @Inject BookmarkModel mManager; + @Inject BookmarkModel mBookmarkModel; + @Inject FaviconModel mFaviconModel; @NonNull private final Bitmap mFolderIcon; @NonNull private final String mTitle; @@ -104,7 +114,8 @@ public final class BookmarkPage { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - cacheDefaultFolderIcon(); + cacheIcon(mFolderIcon, getFaviconFile(mApp)); + cacheIcon(mFaviconModel.getDefaultBitmapForString(null), getDefaultIconFile(mApp)); buildBookmarkPage(null); File bookmarkWebPage = getBookmarkPage(mApp, null); @@ -115,12 +126,12 @@ public final class BookmarkPage { }); } - private void cacheDefaultFolderIcon() { + private void cacheIcon(@NonNull Bitmap icon, @NonNull File file) { FileOutputStream outputStream = null; try { - outputStream = new FileOutputStream(getFaviconFile(mApp)); - mFolderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); - mFolderIcon.recycle(); + outputStream = new FileOutputStream(file); + icon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + icon.recycle(); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { @@ -129,14 +140,14 @@ public final class BookmarkPage { } private void buildBookmarkPage(@Nullable final String folder) { - mManager.getBookmarksFromFolderSorted(folder) + mBookmarkModel.getBookmarksFromFolderSorted(folder) .subscribe(new SingleOnSubscribe>() { @Override public void onItem(@Nullable final List list) { Preconditions.checkNonNull(list); if (folder == null) { - mManager.getFoldersSorted() + mBookmarkModel.getFoldersSorted() .subscribe(new SingleOnSubscribe>() { @Override public void onItem(@Nullable List item) { @@ -171,9 +182,26 @@ public final class BookmarkPage { bookmarkBuilder.append(folderIconPath); buildBookmarkPage(item.getTitle()); } else { + + Uri bookmarkUri = FaviconUtils.safeUri(item.getUrl()); + + String faviconFileUrl; + + if (bookmarkUri != null) { + File faviconFile = FaviconModel.getFaviconCacheFile(mApp, bookmarkUri); + if (!faviconFile.exists()) { + mFaviconModel.cacheFaviconForUrl(mFaviconModel.getDefaultBitmapForString(item.getTitle()), item.getUrl()) + .subscribe(); + } + + faviconFileUrl = Constants.FILE + faviconFile; + } else { + faviconFileUrl = Constants.FILE + getDefaultIconFile(mApp); + } + + bookmarkBuilder.append(item.getUrl()); - bookmarkBuilder.append(PART2).append(PART3); - bookmarkBuilder.append(item.getUrl()); + bookmarkBuilder.append(PART2).append(faviconFileUrl); } bookmarkBuilder.append(PART4); bookmarkBuilder.append(item.getTitle()); diff --git a/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java b/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java index 3dbfaad..ecca925 100644 --- a/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java +++ b/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java @@ -2,10 +2,13 @@ package acr.browser.lightning.favicon; import android.app.Application; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; +import android.text.TextUtils; import android.util.Log; import android.util.LruCache; @@ -38,7 +41,7 @@ public class FaviconModel { private static final String TAG = "FaviconModel"; - @NonNull private final ImageFetcher mImageFetcher; + @NonNull private final BitmapFactory.Options mLoaderOptions = new BitmapFactory.Options(); @NonNull private final Application mApplication; @NonNull private final LruCache mFaviconCache = new LruCache((int) FileUtils.megabytesToBytes(1)) { @Override @@ -51,7 +54,6 @@ public class FaviconModel { @Inject FaviconModel(@NonNull Application application) { - mImageFetcher = new ImageFetcher(); mApplication = application; mBookmarkIconSize = mApplication.getResources().getDimensionPixelSize(R.dimen.bookmark_item_icon_size); } @@ -75,10 +77,12 @@ public class FaviconModel { } @NonNull - private Bitmap getDefaultBitmapForCharacter(@NonNull Character character) { - @ColorInt int defaultFaviconColor = DrawableUtils.characterToColorHash(character, mApplication); + public Bitmap getDefaultBitmapForString(@Nullable String title) { + Character firstTitleCharacter = !TextUtils.isEmpty(title) ? title.charAt(0) : '?'; - return DrawableUtils.getRoundedLetterImage(character, + @ColorInt int defaultFaviconColor = DrawableUtils.characterToColorHash(firstTitleCharacter, mApplication); + + return DrawableUtils.getRoundedLetterImage(firstTitleCharacter, mBookmarkIconSize, mBookmarkIconSize, defaultFaviconColor); @@ -109,8 +113,9 @@ public class FaviconModel { * @param uri the URI to use as a unique identifier. * @return a valid cache file. */ + @WorkerThread @NonNull - private static File createFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) { + public static File getFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) { FaviconUtils.assertUriSafe(uri); String hash = String.valueOf(uri.getHost().hashCode()); @@ -122,16 +127,13 @@ public class FaviconModel { * Retrieves the favicon for a URL, * may be from network or cache. * - * @param url The URL that we should retrieve the - * favicon for. - * @param title The title for the web page. - * @param allowGoogleService True to allow grabbing favicons - * from Google, false otherwise. @return an observable that emits a bitmap if one is found, + * @param url The URL that we should retrieve the + * favicon for. + * @param title The title for the web page. */ @NonNull public Single faviconForUrl(@NonNull final String url, - @NonNull final String title, - final boolean allowGoogleService) { + @NonNull final String title) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { @@ -139,7 +141,7 @@ public class FaviconModel { if (uri == null) { - Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForCharacter('?')); + Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForString(title)); subscriber.onItem(newFavicon); subscriber.onComplete(); @@ -147,25 +149,19 @@ public class FaviconModel { return; } - Character firstTitleCharacter = !title.isEmpty() ? title.charAt(0) : '?'; - - - File faviconCacheFile = createFaviconCacheFile(mApplication, uri); + File faviconCacheFile = getFaviconCacheFile(mApplication, uri); Bitmap favicon = getFaviconFromMemCache(url); if (faviconCacheFile.exists() && favicon == null) { - favicon = mImageFetcher.retrieveFaviconFromCache(faviconCacheFile); + favicon = BitmapFactory.decodeFile(faviconCacheFile.getPath(), mLoaderOptions); if (favicon != null) { addFaviconToMemCache(url, favicon); } } - if (favicon == null) { - // TODO: 6/5/17 figure out if optimistic favicon retrieval should be added back or dropped - // favicon = mImageFetcher.retrieveBitmapFromDomain(uri); - } else { + if (favicon != null) { Bitmap newFavicon = Utils.padFavicon(favicon); subscriber.onItem(newFavicon); @@ -174,18 +170,7 @@ public class FaviconModel { return; } - // if (favicon == null && allowGoogleService) { - // favicon = mImageFetcher.retrieveBitmapFromGoogle(uri); - // } - - // if (favicon != null) { - // addFaviconToMemCache(url, favicon); - // cacheFaviconForUrl(favicon, url).subscribe(); - // } - - // if (favicon == null) { - favicon = getDefaultBitmapForCharacter(firstTitleCharacter); - // } + favicon = getDefaultBitmapForString(title); Bitmap newFavicon = Utils.padFavicon(favicon); @@ -220,7 +205,7 @@ public class FaviconModel { FileOutputStream fos = null; try { - File image = createFaviconCacheFile(mApplication, uri); + File image = getFaviconCacheFile(mApplication, uri); fos = new FileOutputStream(image); favicon.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); diff --git a/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java b/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java index a32420b..19cd324 100644 --- a/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java +++ b/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java @@ -8,9 +8,9 @@ import android.text.TextUtils; /** * Simple utils for favicon fetching. */ -class FaviconUtils { +public class FaviconUtils { @Nullable - static Uri safeUri(@NonNull String url) { + public static Uri safeUri(@NonNull String url) { if (TextUtils.isEmpty(url)) { return null; } diff --git a/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java b/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java deleted file mode 100644 index 337e408..0000000 --- a/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java +++ /dev/null @@ -1,110 +0,0 @@ -package acr.browser.lightning.favicon; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import acr.browser.lightning.utils.Utils; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; - -/** - * An image fetcher that creates image - * loading requests on demand. - */ -class ImageFetcher { - - private static final String TAG = "ImageFetcher"; - - @NonNull private final BitmapFactory.Options mLoaderOptions = new BitmapFactory.Options(); - @NonNull private final OkHttpClient mHttpClient = new OkHttpClient(); - - ImageFetcher() {} - - @Nullable - Bitmap retrieveFaviconFromCache(@NonNull File cacheFile) { - return BitmapFactory.decodeFile(cacheFile.getPath(), mLoaderOptions); - } - - @Nullable - Bitmap retrieveBitmapFromDomain(@NonNull Uri uri) { - FaviconUtils.assertUriSafe(uri); - - String faviconUrlGuess = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico"; - - return retrieveBitmapFromUrl(faviconUrlGuess); - } - - @Nullable - Bitmap retrieveBitmapFromGoogle(@NonNull Uri uri) { - FaviconUtils.assertUriSafe(uri); - - String googleFaviconUrl = "https://www.google.com/s2/favicons?domain_url=" + uri.getHost(); - - return retrieveBitmapFromUrl(googleFaviconUrl); - } - - @Nullable - private Bitmap retrieveBitmapFromUrl(@NonNull String url) { - Bitmap icon = null; - - InputStream boundsStream = null; - InputStream iconStream = null; - - try { - mLoaderOptions.inSampleSize = 1; - mLoaderOptions.inJustDecodeBounds = true; - - Request imageRequest = new Request.Builder().url(url).build(); - - Response boundsResponse = mHttpClient.newCall(imageRequest).execute(); - ResponseBody boundsBody = boundsResponse.body(); - - if (boundsBody == null) { - return null; - } - - boundsStream = boundsBody.byteStream(); - - BitmapFactory.decodeStream(boundsStream, null, mLoaderOptions); - - boundsBody.close(); - - int size = Utils.dpToPx(24); - - mLoaderOptions.inSampleSize = Utils.calculateInSampleSize(mLoaderOptions, size, size); - mLoaderOptions.inJustDecodeBounds = false; - - Response imageResponse = mHttpClient.newCall(imageRequest).execute(); - - ResponseBody imageBody = imageResponse.body(); - - if (imageBody == null) { - return null; - } - - iconStream = imageBody.byteStream(); - - icon = BitmapFactory.decodeStream(iconStream, null, mLoaderOptions); - - imageBody.close(); - } catch (IOException exception) { - Log.d(TAG, "Unable to download icon: " + url); - } finally { - Utils.close(boundsStream); - Utils.close(iconStream); - } - - return icon; - } - -} diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java index 7668c8e..cf62cec 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java @@ -537,7 +537,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, Subscription oldSubscription = mFaviconFetchSubscriptions.get(url); SubscriptionUtils.safeUnsubscribe(oldSubscription); - final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, web.getTitle(), true) + final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, web.getTitle()) .subscribeOn(Schedulers.worker()) .observeOn(Schedulers.main()) .subscribe(new SingleOnSubscribe() {