Browse Source

Getting rid of google favicon fetching and using generated favicon as fallbacks

master
anthony restaino 7 years ago
parent
commit
1a1efc9da6
  1. 48
      app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
  2. 57
      app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java
  3. 4
      app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java
  4. 110
      app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java
  5. 2
      app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java

48
app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java

@ -6,6 +6,7 @@ package acr.browser.lightning.constant; @@ -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; @@ -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 { @@ -75,6 +78,7 @@ public final class BookmarkPage {
private static final String END = "</div></body></html>";
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 { @@ -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 { @@ -104,7 +114,8 @@ public final class BookmarkPage {
return Single.create(new SingleAction<String>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<String> 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 { @@ -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 { @@ -129,14 +140,14 @@ public final class BookmarkPage {
}
private void buildBookmarkPage(@Nullable final String folder) {
mManager.getBookmarksFromFolderSorted(folder)
mBookmarkModel.getBookmarksFromFolderSorted(folder)
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
@Override
public void onItem(@Nullable final List<HistoryItem> list) {
Preconditions.checkNonNull(list);
if (folder == null) {
mManager.getFoldersSorted()
mBookmarkModel.getFoldersSorted()
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
@Override
public void onItem(@Nullable List<HistoryItem> item) {
@ -171,9 +182,26 @@ public final class BookmarkPage { @@ -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());

57
app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java

@ -2,10 +2,13 @@ package acr.browser.lightning.favicon; @@ -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 { @@ -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<String, Bitmap> mFaviconCache = new LruCache<String, Bitmap>((int) FileUtils.megabytesToBytes(1)) {
@Override
@ -51,7 +54,6 @@ public class FaviconModel { @@ -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 { @@ -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 { @@ -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 { @@ -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<Bitmap> faviconForUrl(@NonNull final String url,
@NonNull final String title,
final boolean allowGoogleService) {
@NonNull final String title) {
return Single.create(new SingleAction<Bitmap>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
@ -139,7 +141,7 @@ public class FaviconModel { @@ -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 { @@ -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 { @@ -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 { @@ -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();

4
app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java

@ -8,9 +8,9 @@ import android.text.TextUtils; @@ -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;
}

110
app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java

@ -1,110 +0,0 @@ @@ -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;
}
}

2
app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java

@ -537,7 +537,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, @@ -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<Bitmap>() {

Loading…
Cancel
Save