Getting rid of google favicon fetching and using generated favicon as fallbacks
This commit is contained in:
parent
003954773c
commit
1a1efc9da6
@ -6,6 +6,7 @@ package acr.browser.lightning.constant;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -28,6 +29,8 @@ import acr.browser.lightning.R;
|
|||||||
import acr.browser.lightning.app.BrowserApp;
|
import acr.browser.lightning.app.BrowserApp;
|
||||||
import acr.browser.lightning.database.HistoryItem;
|
import acr.browser.lightning.database.HistoryItem;
|
||||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
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.Preconditions;
|
||||||
import acr.browser.lightning.utils.ThemeUtils;
|
import acr.browser.lightning.utils.ThemeUtils;
|
||||||
import acr.browser.lightning.utils.Utils;
|
import acr.browser.lightning.utils.Utils;
|
||||||
@ -75,6 +78,7 @@ public final class BookmarkPage {
|
|||||||
private static final String END = "</div></body></html>";
|
private static final String END = "</div></body></html>";
|
||||||
|
|
||||||
private static final String FOLDER_ICON = "folder.png";
|
private static final String FOLDER_ICON = "folder.png";
|
||||||
|
private static final String DEFAULT_ICON = "default.png";
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static File getBookmarkPage(@NonNull Application application, @Nullable String folder) {
|
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);
|
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 Application mApp;
|
||||||
@Inject BookmarkModel mManager;
|
@Inject BookmarkModel mBookmarkModel;
|
||||||
|
@Inject FaviconModel mFaviconModel;
|
||||||
|
|
||||||
@NonNull private final Bitmap mFolderIcon;
|
@NonNull private final Bitmap mFolderIcon;
|
||||||
@NonNull private final String mTitle;
|
@NonNull private final String mTitle;
|
||||||
@ -104,7 +114,8 @@ public final class BookmarkPage {
|
|||||||
return Single.create(new SingleAction<String>() {
|
return Single.create(new SingleAction<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(@NonNull SingleSubscriber<String> subscriber) {
|
public void onSubscribe(@NonNull SingleSubscriber<String> subscriber) {
|
||||||
cacheDefaultFolderIcon();
|
cacheIcon(mFolderIcon, getFaviconFile(mApp));
|
||||||
|
cacheIcon(mFaviconModel.getDefaultBitmapForString(null), getDefaultIconFile(mApp));
|
||||||
buildBookmarkPage(null);
|
buildBookmarkPage(null);
|
||||||
|
|
||||||
File bookmarkWebPage = getBookmarkPage(mApp, 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;
|
FileOutputStream outputStream = null;
|
||||||
try {
|
try {
|
||||||
outputStream = new FileOutputStream(getFaviconFile(mApp));
|
outputStream = new FileOutputStream(file);
|
||||||
mFolderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
|
icon.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
|
||||||
mFolderIcon.recycle();
|
icon.recycle();
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
@ -129,14 +140,14 @@ public final class BookmarkPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void buildBookmarkPage(@Nullable final String folder) {
|
private void buildBookmarkPage(@Nullable final String folder) {
|
||||||
mManager.getBookmarksFromFolderSorted(folder)
|
mBookmarkModel.getBookmarksFromFolderSorted(folder)
|
||||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onItem(@Nullable final List<HistoryItem> list) {
|
public void onItem(@Nullable final List<HistoryItem> list) {
|
||||||
Preconditions.checkNonNull(list);
|
Preconditions.checkNonNull(list);
|
||||||
|
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
mManager.getFoldersSorted()
|
mBookmarkModel.getFoldersSorted()
|
||||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onItem(@Nullable List<HistoryItem> item) {
|
public void onItem(@Nullable List<HistoryItem> item) {
|
||||||
@ -171,9 +182,26 @@ public final class BookmarkPage {
|
|||||||
bookmarkBuilder.append(folderIconPath);
|
bookmarkBuilder.append(folderIconPath);
|
||||||
buildBookmarkPage(item.getTitle());
|
buildBookmarkPage(item.getTitle());
|
||||||
} else {
|
} 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(item.getUrl());
|
||||||
bookmarkBuilder.append(PART2).append(PART3);
|
bookmarkBuilder.append(PART2).append(faviconFileUrl);
|
||||||
bookmarkBuilder.append(item.getUrl());
|
|
||||||
}
|
}
|
||||||
bookmarkBuilder.append(PART4);
|
bookmarkBuilder.append(PART4);
|
||||||
bookmarkBuilder.append(item.getTitle());
|
bookmarkBuilder.append(item.getTitle());
|
||||||
|
@ -2,10 +2,13 @@ package acr.browser.lightning.favicon;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.ColorInt;
|
import android.support.annotation.ColorInt;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.WorkerThread;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.LruCache;
|
import android.util.LruCache;
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ public class FaviconModel {
|
|||||||
|
|
||||||
private static final String TAG = "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 Application mApplication;
|
||||||
@NonNull private final LruCache<String, Bitmap> mFaviconCache = new LruCache<String, Bitmap>((int) FileUtils.megabytesToBytes(1)) {
|
@NonNull private final LruCache<String, Bitmap> mFaviconCache = new LruCache<String, Bitmap>((int) FileUtils.megabytesToBytes(1)) {
|
||||||
@Override
|
@Override
|
||||||
@ -51,7 +54,6 @@ public class FaviconModel {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FaviconModel(@NonNull Application application) {
|
FaviconModel(@NonNull Application application) {
|
||||||
mImageFetcher = new ImageFetcher();
|
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
mBookmarkIconSize = mApplication.getResources().getDimensionPixelSize(R.dimen.bookmark_item_icon_size);
|
mBookmarkIconSize = mApplication.getResources().getDimensionPixelSize(R.dimen.bookmark_item_icon_size);
|
||||||
}
|
}
|
||||||
@ -75,10 +77,12 @@ public class FaviconModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Bitmap getDefaultBitmapForCharacter(@NonNull Character character) {
|
public Bitmap getDefaultBitmapForString(@Nullable String title) {
|
||||||
@ColorInt int defaultFaviconColor = DrawableUtils.characterToColorHash(character, mApplication);
|
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,
|
||||||
mBookmarkIconSize,
|
mBookmarkIconSize,
|
||||||
defaultFaviconColor);
|
defaultFaviconColor);
|
||||||
@ -109,8 +113,9 @@ public class FaviconModel {
|
|||||||
* @param uri the URI to use as a unique identifier.
|
* @param uri the URI to use as a unique identifier.
|
||||||
* @return a valid cache file.
|
* @return a valid cache file.
|
||||||
*/
|
*/
|
||||||
|
@WorkerThread
|
||||||
@NonNull
|
@NonNull
|
||||||
private static File createFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) {
|
public static File getFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) {
|
||||||
FaviconUtils.assertUriSafe(uri);
|
FaviconUtils.assertUriSafe(uri);
|
||||||
|
|
||||||
String hash = String.valueOf(uri.getHost().hashCode());
|
String hash = String.valueOf(uri.getHost().hashCode());
|
||||||
@ -122,16 +127,13 @@ public class FaviconModel {
|
|||||||
* Retrieves the favicon for a URL,
|
* Retrieves the favicon for a URL,
|
||||||
* may be from network or cache.
|
* may be from network or cache.
|
||||||
*
|
*
|
||||||
* @param url The URL that we should retrieve the
|
* @param url The URL that we should retrieve the
|
||||||
* favicon for.
|
* favicon for.
|
||||||
* @param title The title for the web page.
|
* @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,
|
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Single<Bitmap> faviconForUrl(@NonNull final String url,
|
public Single<Bitmap> faviconForUrl(@NonNull final String url,
|
||||||
@NonNull final String title,
|
@NonNull final String title) {
|
||||||
final boolean allowGoogleService) {
|
|
||||||
return Single.create(new SingleAction<Bitmap>() {
|
return Single.create(new SingleAction<Bitmap>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
|
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
|
||||||
@ -139,7 +141,7 @@ public class FaviconModel {
|
|||||||
|
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
|
|
||||||
Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForCharacter('?'));
|
Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForString(title));
|
||||||
|
|
||||||
subscriber.onItem(newFavicon);
|
subscriber.onItem(newFavicon);
|
||||||
subscriber.onComplete();
|
subscriber.onComplete();
|
||||||
@ -147,25 +149,19 @@ public class FaviconModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Character firstTitleCharacter = !title.isEmpty() ? title.charAt(0) : '?';
|
File faviconCacheFile = getFaviconCacheFile(mApplication, uri);
|
||||||
|
|
||||||
|
|
||||||
File faviconCacheFile = createFaviconCacheFile(mApplication, uri);
|
|
||||||
|
|
||||||
Bitmap favicon = getFaviconFromMemCache(url);
|
Bitmap favicon = getFaviconFromMemCache(url);
|
||||||
|
|
||||||
if (faviconCacheFile.exists() && favicon == null) {
|
if (faviconCacheFile.exists() && favicon == null) {
|
||||||
favicon = mImageFetcher.retrieveFaviconFromCache(faviconCacheFile);
|
favicon = BitmapFactory.decodeFile(faviconCacheFile.getPath(), mLoaderOptions);
|
||||||
|
|
||||||
if (favicon != null) {
|
if (favicon != null) {
|
||||||
addFaviconToMemCache(url, favicon);
|
addFaviconToMemCache(url, favicon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (favicon == null) {
|
if (favicon != null) {
|
||||||
// TODO: 6/5/17 figure out if optimistic favicon retrieval should be added back or dropped
|
|
||||||
// favicon = mImageFetcher.retrieveBitmapFromDomain(uri);
|
|
||||||
} else {
|
|
||||||
Bitmap newFavicon = Utils.padFavicon(favicon);
|
Bitmap newFavicon = Utils.padFavicon(favicon);
|
||||||
|
|
||||||
subscriber.onItem(newFavicon);
|
subscriber.onItem(newFavicon);
|
||||||
@ -174,18 +170,7 @@ public class FaviconModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (favicon == null && allowGoogleService) {
|
favicon = getDefaultBitmapForString(title);
|
||||||
// favicon = mImageFetcher.retrieveBitmapFromGoogle(uri);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (favicon != null) {
|
|
||||||
// addFaviconToMemCache(url, favicon);
|
|
||||||
// cacheFaviconForUrl(favicon, url).subscribe();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (favicon == null) {
|
|
||||||
favicon = getDefaultBitmapForCharacter(firstTitleCharacter);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Bitmap newFavicon = Utils.padFavicon(favicon);
|
Bitmap newFavicon = Utils.padFavicon(favicon);
|
||||||
|
|
||||||
@ -220,7 +205,7 @@ public class FaviconModel {
|
|||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File image = createFaviconCacheFile(mApplication, uri);
|
File image = getFaviconCacheFile(mApplication, uri);
|
||||||
fos = new FileOutputStream(image);
|
fos = new FileOutputStream(image);
|
||||||
favicon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
favicon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
fos.flush();
|
fos.flush();
|
||||||
|
@ -8,9 +8,9 @@ import android.text.TextUtils;
|
|||||||
/**
|
/**
|
||||||
* Simple utils for favicon fetching.
|
* Simple utils for favicon fetching.
|
||||||
*/
|
*/
|
||||||
class FaviconUtils {
|
public class FaviconUtils {
|
||||||
@Nullable
|
@Nullable
|
||||||
static Uri safeUri(@NonNull String url) {
|
public static Uri safeUri(@NonNull String url) {
|
||||||
if (TextUtils.isEmpty(url)) {
|
if (TextUtils.isEmpty(url)) {
|
||||||
return null;
|
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -537,7 +537,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
|||||||
Subscription oldSubscription = mFaviconFetchSubscriptions.get(url);
|
Subscription oldSubscription = mFaviconFetchSubscriptions.get(url);
|
||||||
SubscriptionUtils.safeUnsubscribe(oldSubscription);
|
SubscriptionUtils.safeUnsubscribe(oldSubscription);
|
||||||
|
|
||||||
final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, web.getTitle(), true)
|
final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, web.getTitle())
|
||||||
.subscribeOn(Schedulers.worker())
|
.subscribeOn(Schedulers.worker())
|
||||||
.observeOn(Schedulers.main())
|
.observeOn(Schedulers.main())
|
||||||
.subscribe(new SingleOnSubscribe<Bitmap>() {
|
.subscribe(new SingleOnSubscribe<Bitmap>() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user