Dropping async task for bonsai in image downloading
This commit is contained in:
parent
0819d35711
commit
3c133748e9
@ -66,7 +66,7 @@ dexcount {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
// support libraries
|
// support libraries
|
||||||
def supportLibVersion = '25.3.0'
|
def supportLibVersion = '25.3.1'
|
||||||
compile "com.android.support:palette-v7:$supportLibVersion"
|
compile "com.android.support:palette-v7:$supportLibVersion"
|
||||||
compile "com.android.support:appcompat-v7:$supportLibVersion"
|
compile "com.android.support:appcompat-v7:$supportLibVersion"
|
||||||
compile "com.android.support:design:$supportLibVersion"
|
compile "com.android.support:design:$supportLibVersion"
|
||||||
|
@ -7,6 +7,7 @@ import acr.browser.lightning.activity.ReadingActivity;
|
|||||||
import acr.browser.lightning.activity.TabsManager;
|
import acr.browser.lightning.activity.TabsManager;
|
||||||
import acr.browser.lightning.activity.ThemableBrowserActivity;
|
import acr.browser.lightning.activity.ThemableBrowserActivity;
|
||||||
import acr.browser.lightning.activity.ThemableSettingsActivity;
|
import acr.browser.lightning.activity.ThemableSettingsActivity;
|
||||||
|
import acr.browser.lightning.view.ImageDownloader;
|
||||||
import acr.browser.lightning.browser.BrowserPresenter;
|
import acr.browser.lightning.browser.BrowserPresenter;
|
||||||
import acr.browser.lightning.constant.BookmarkPage;
|
import acr.browser.lightning.constant.BookmarkPage;
|
||||||
import acr.browser.lightning.constant.HistoryPage;
|
import acr.browser.lightning.constant.HistoryPage;
|
||||||
@ -76,4 +77,6 @@ public interface AppComponent {
|
|||||||
|
|
||||||
void inject(SuggestionsAdapter suggestionsAdapter);
|
void inject(SuggestionsAdapter suggestionsAdapter);
|
||||||
|
|
||||||
|
void inject(ImageDownloader imageDownloader);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package acr.browser.lightning.async;
|
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created 9/27/2015 Anthony Restaino
|
|
||||||
*/
|
|
||||||
public class AsyncExecutor implements Executor {
|
|
||||||
|
|
||||||
private static final String TAG = AsyncExecutor.class.getSimpleName();
|
|
||||||
private static final AsyncExecutor INSTANCE = new AsyncExecutor();
|
|
||||||
private final Queue<Runnable> mQueue = new ArrayDeque<>(1);
|
|
||||||
private final ExecutorService mExecutor = Executors.newFixedThreadPool(4);
|
|
||||||
|
|
||||||
private AsyncExecutor() {}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static AsyncExecutor getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void notifyThreadFinish() {
|
|
||||||
if (mQueue.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Runnable runnable = mQueue.remove();
|
|
||||||
execute(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
mExecutor.shutdownNow();
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(@NonNull Runnable command) {
|
|
||||||
try {
|
|
||||||
mExecutor.execute(command);
|
|
||||||
} catch (RejectedExecutionException ignored) {
|
|
||||||
mQueue.add(command);
|
|
||||||
Log.d(TAG, "Thread was enqueued");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,6 +35,7 @@ import com.anthonycr.bonsai.SingleSubscriber;
|
|||||||
import com.squareup.otto.Bus;
|
import com.squareup.otto.Bus;
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -44,8 +45,7 @@ import acr.browser.lightning.R;
|
|||||||
import acr.browser.lightning.activity.ReadingActivity;
|
import acr.browser.lightning.activity.ReadingActivity;
|
||||||
import acr.browser.lightning.activity.TabsManager;
|
import acr.browser.lightning.activity.TabsManager;
|
||||||
import acr.browser.lightning.app.BrowserApp;
|
import acr.browser.lightning.app.BrowserApp;
|
||||||
import acr.browser.lightning.async.AsyncExecutor;
|
import acr.browser.lightning.view.ImageDownloader;
|
||||||
import acr.browser.lightning.async.ImageDownloadTask;
|
|
||||||
import acr.browser.lightning.browser.BookmarksView;
|
import acr.browser.lightning.browser.BookmarksView;
|
||||||
import acr.browser.lightning.bus.BookmarkEvents;
|
import acr.browser.lightning.bus.BookmarkEvents;
|
||||||
import acr.browser.lightning.constant.Constants;
|
import acr.browser.lightning.constant.Constants;
|
||||||
@ -74,6 +74,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
|||||||
|
|
||||||
@Inject PreferenceManager mPreferenceManager;
|
@Inject PreferenceManager mPreferenceManager;
|
||||||
|
|
||||||
|
private ImageDownloader mImageDownloader;
|
||||||
|
|
||||||
private TabsManager mTabsManager;
|
private TabsManager mTabsManager;
|
||||||
|
|
||||||
private UIController mUiController;
|
private UIController mUiController;
|
||||||
@ -124,7 +126,9 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
|||||||
mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme);
|
mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme);
|
||||||
mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme);
|
mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme);
|
||||||
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) :
|
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) :
|
||||||
ThemeUtils.getIconLightThemeColor(context);
|
ThemeUtils.getIconLightThemeColor(context);
|
||||||
|
|
||||||
|
mImageDownloader = new ImageDownloader(mWebpageBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TabsManager getTabsManager() {
|
private TabsManager getTabsManager() {
|
||||||
@ -222,7 +226,9 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
|||||||
mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme);
|
mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme);
|
||||||
mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme);
|
mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme);
|
||||||
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) :
|
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) :
|
||||||
ThemeUtils.getIconLightThemeColor(activity);
|
ThemeUtils.getIconLightThemeColor(activity);
|
||||||
|
|
||||||
|
mImageDownloader = new ImageDownloader(mWebpageBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBookmarkIndicator(final String url) {
|
private void updateBookmarkIndicator(final String url) {
|
||||||
@ -389,14 +395,31 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
|||||||
|
|
||||||
ViewCompat.jumpDrawablesToCurrentState(row);
|
ViewCompat.jumpDrawablesToCurrentState(row);
|
||||||
|
|
||||||
HistoryItem web = mBookmarks.get(position);
|
final HistoryItem web = mBookmarks.get(position);
|
||||||
holder.txtTitle.setText(web.getTitle());
|
holder.txtTitle.setText(web.getTitle());
|
||||||
if (web.isFolder()) {
|
if (web.isFolder()) {
|
||||||
holder.favicon.setImageBitmap(mFolderBitmap);
|
holder.favicon.setImageBitmap(mFolderBitmap);
|
||||||
} else if (web.getBitmap() == null) {
|
} else if (web.getBitmap() == null) {
|
||||||
holder.favicon.setImageBitmap(mWebpageBitmap);
|
holder.favicon.setImageBitmap(mWebpageBitmap);
|
||||||
new ImageDownloadTask(holder.favicon, web, mWebpageBitmap, BrowserApp.get(context))
|
holder.favicon.setTag(web.getUrl().hashCode());
|
||||||
.executeOnExecutor(AsyncExecutor.getInstance());
|
|
||||||
|
final String url = web.getUrl();
|
||||||
|
final WeakReference<ImageView> imageViewReference = new WeakReference<>(holder.favicon);
|
||||||
|
mImageDownloader.newImageRequest(url)
|
||||||
|
.subscribeOn(Schedulers.worker())
|
||||||
|
.observeOn(Schedulers.main())
|
||||||
|
.subscribe(new SingleOnSubscribe<Bitmap>() {
|
||||||
|
@Override
|
||||||
|
public void onItem(@Nullable Bitmap item) {
|
||||||
|
ImageView imageView = imageViewReference.get();
|
||||||
|
Object tag = imageView != null ? imageView.getTag() : null;
|
||||||
|
if (tag != null && tag.equals(url.hashCode())) {
|
||||||
|
imageView.setImageBitmap(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
web.setBitmap(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
holder.favicon.setImageBitmap(web.getBitmap());
|
holder.favicon.setImageBitmap(web.getBitmap());
|
||||||
}
|
}
|
||||||
|
@ -1,69 +1,91 @@
|
|||||||
package acr.browser.lightning.async;
|
package acr.browser.lightning.view;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import com.anthonycr.bonsai.Schedulers;
|
import com.anthonycr.bonsai.Single;
|
||||||
|
import com.anthonycr.bonsai.SingleAction;
|
||||||
|
import com.anthonycr.bonsai.SingleSubscriber;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import acr.browser.lightning.app.BrowserApp;
|
||||||
import acr.browser.lightning.constant.Constants;
|
import acr.browser.lightning.constant.Constants;
|
||||||
import acr.browser.lightning.database.HistoryItem;
|
|
||||||
import acr.browser.lightning.utils.Utils;
|
import acr.browser.lightning.utils.Utils;
|
||||||
|
|
||||||
public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
/**
|
||||||
|
* An ImageDownloader that creates image
|
||||||
|
* loading requests on demand.
|
||||||
|
*/
|
||||||
|
public class ImageDownloader {
|
||||||
|
|
||||||
private static final String TAG = ImageDownloadTask.class.getSimpleName();
|
private static final String TAG = "ImageDownloader";
|
||||||
@NonNull private final WeakReference<ImageView> mFaviconImage;
|
|
||||||
@NonNull private final Application mContext;
|
|
||||||
@NonNull private final HistoryItem mWeb;
|
|
||||||
private final String mUrl;
|
|
||||||
@NonNull private final Bitmap mDefaultBitmap;
|
|
||||||
|
|
||||||
public ImageDownloadTask(@NonNull ImageView bmImage,
|
@Inject Application mApp;
|
||||||
@NonNull HistoryItem web,
|
|
||||||
@NonNull Bitmap defaultBitmap,
|
@NonNull private Bitmap mDefaultBitmap;
|
||||||
@NonNull Application context) {
|
|
||||||
// Set a tag on the ImageView so we know if the view
|
public ImageDownloader(@NonNull Bitmap defaultBitmap) {
|
||||||
// has gone out of scope and should not be used
|
mDefaultBitmap = defaultBitmap;
|
||||||
bmImage.setTag(web.getUrl().hashCode());
|
BrowserApp.getAppComponent().inject(this);
|
||||||
this.mFaviconImage = new WeakReference<>(bmImage);
|
}
|
||||||
this.mWeb = web;
|
|
||||||
this.mUrl = web.getUrl();
|
/**
|
||||||
this.mDefaultBitmap = defaultBitmap;
|
* Creates a new image request for the given url.
|
||||||
this.mContext = context;
|
* Emits the bitmap associated with that url, or
|
||||||
|
* the default bitmap if none was found.
|
||||||
|
*
|
||||||
|
* @param url the url for which to retrieve the bitmap.
|
||||||
|
* @return a single that emits the bitmap that was found.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Single<Bitmap> newImageRequest(@Nullable final String url) {
|
||||||
|
return Single.create(new SingleAction<Bitmap>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
|
||||||
|
Bitmap favicon = retrieveBitmap(mApp, mDefaultBitmap, url);
|
||||||
|
|
||||||
|
Bitmap paddedFavicon = Utils.padFavicon(favicon);
|
||||||
|
|
||||||
|
subscriber.onItem(paddedFavicon);
|
||||||
|
subscriber.onComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
private static Bitmap retrieveBitmap(@NonNull Application app,
|
||||||
protected Bitmap doInBackground(Void... params) {
|
@NonNull Bitmap defaultBitmap,
|
||||||
Bitmap mIcon = null;
|
@Nullable String url) {
|
||||||
|
|
||||||
// unique path for each url that is bookmarked.
|
// unique path for each url that is bookmarked.
|
||||||
if (mUrl == null) {
|
if (url == null) {
|
||||||
return mDefaultBitmap;
|
return defaultBitmap;
|
||||||
}
|
}
|
||||||
File cache = mContext.getCacheDir();
|
|
||||||
final Uri uri = Uri.parse(mUrl);
|
Bitmap icon = null;
|
||||||
if (uri.getHost() == null || uri.getScheme() == null) {
|
File cache = app.getCacheDir();
|
||||||
return mDefaultBitmap;
|
final Uri uri = Uri.parse(url);
|
||||||
|
|
||||||
|
if (uri.getHost() == null || uri.getScheme() == null || Constants.FILE.startsWith(uri.getScheme())) {
|
||||||
|
return defaultBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String hash = String.valueOf(uri.getHost().hashCode());
|
final String hash = String.valueOf(uri.getHost().hashCode());
|
||||||
final File image = new File(cache, hash + ".png");
|
final File image = new File(cache, hash + ".png");
|
||||||
final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico";
|
final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico";
|
||||||
if (Constants.FILE.startsWith(uri.getScheme())) {
|
|
||||||
return mDefaultBitmap;
|
|
||||||
}
|
|
||||||
// checks to see if the image exists
|
// checks to see if the image exists
|
||||||
if (!image.exists()) {
|
if (!image.exists()) {
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
@ -79,12 +101,12 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
|||||||
in = connection.getInputStream();
|
in = connection.getInputStream();
|
||||||
|
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
mIcon = BitmapFactory.decodeStream(in);
|
icon = BitmapFactory.decodeStream(in);
|
||||||
}
|
}
|
||||||
// ...and cache it
|
// ...and cache it
|
||||||
if (mIcon != null) {
|
if (icon != null) {
|
||||||
fos = new FileOutputStream(image);
|
fos = new FileOutputStream(image);
|
||||||
mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
fos.flush();
|
fos.flush();
|
||||||
Log.d(Constants.TAG, "Downloaded: " + urlDisplay);
|
Log.d(Constants.TAG, "Downloaded: " + urlDisplay);
|
||||||
}
|
}
|
||||||
@ -97,9 +119,10 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if it exists, retrieve it from the cache
|
// if it exists, retrieve it from the cache
|
||||||
mIcon = BitmapFactory.decodeFile(image.getPath());
|
icon = BitmapFactory.decodeFile(image.getPath());
|
||||||
}
|
}
|
||||||
if (mIcon == null) {
|
|
||||||
|
if (icon == null) {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
@ -113,12 +136,12 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
|||||||
in = connection.getInputStream();
|
in = connection.getInputStream();
|
||||||
|
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
mIcon = BitmapFactory.decodeStream(in);
|
icon = BitmapFactory.decodeStream(in);
|
||||||
}
|
}
|
||||||
// ...and cache it
|
// ...and cache it
|
||||||
if (mIcon != null) {
|
if (icon != null) {
|
||||||
fos = new FileOutputStream(image);
|
fos = new FileOutputStream(image);
|
||||||
mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
fos.flush();
|
fos.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,28 +152,11 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
|||||||
Utils.close(fos);
|
Utils.close(fos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mIcon == null) {
|
|
||||||
return mDefaultBitmap;
|
if (icon == null) {
|
||||||
|
return defaultBitmap;
|
||||||
} else {
|
} else {
|
||||||
return mIcon;
|
return icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Bitmap bitmap) {
|
|
||||||
super.onPostExecute(bitmap);
|
|
||||||
AsyncExecutor.getInstance().notifyThreadFinish();
|
|
||||||
final Bitmap fav = Utils.padFavicon(bitmap);
|
|
||||||
final ImageView view = mFaviconImage.get();
|
|
||||||
if (view != null && view.getTag().equals(mWeb.getUrl().hashCode())) {
|
|
||||||
Schedulers.main().execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
view.setImageBitmap(fav);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mWeb.setBitmap(fav);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user