package com.gnod.geekr.tool.manager;
import java.lang.ref.WeakReference;
import android.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.Log;
import android.widget.ImageView;
import com.gnod.geekr.BuildConfig;
public abstract class ImageWorker {
private static final int MESSAGE_CLEAR = 0;
private static final int MESSAGE_INIT_DISK_CACHE = 1;
private static final int MESSAGE_FLUSH = 2;
private static final int MESSAGE_CLOSE = 3;
private static final int FADE_IN_TIME = 200;
private static final String TAG = "ImageWorker";
protected Resources mResources;
private ImageCache mImageCache;
private Bitmap mDefaultBitmap;
private boolean mFadeInBitmap = false;
private boolean mExitTasksEarly = false;
private final Object mPauseWorkLock = new Object();
private boolean mPauseWork = false;
protected ImageWorker(Context context, ImageCache imageCache) {
mResources = context.getResources();
addImageCache(imageCache);
}
public void addImageCache(ImageCache imageCache) {
mImageCache = imageCache;
initDiskCache();
}
public void loadImage(Object data, ImageView imageView) {
loadImage(data, imageView, mDefaultBitmap);
}
public void loadImage(Object data, ImageView imageView, int defaultResId) {
BitmapDrawable value = null;
if (mImageCache != null) {
value = mImageCache.getBitmapFromMemCache(String.valueOf(defaultResId));
}
if (value == null) {
value = new BitmapDrawable(mResources, BitmapFactory.decodeResource(mResources, defaultResId));
mImageCache.addToMemCache(String.valueOf(defaultResId), value);
}
loadImage(data, imageView, value.getBitmap());
}
public void loadImage(Object data, ImageView imageView, Bitmap defaultBitmap) {
if (data == null) {
return;
}
BitmapDrawable value = null;
if (mImageCache != null) {
value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
}
if (value != null) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "processBitmap loadImage memory cache hit -- " + data);
}
imageView.setImageDrawable(value);
} else if (cancelPotentailWork(data, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
final AsyncDrawable drawable = new AsyncDrawable(mResources, defaultBitmap, task);
imageView.setImageDrawable(drawable);
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
// framework and slightly modified. Refer to the docs at the top of the class
// for more info on what was changed.
task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
}
}
public void setDefaultImage(Bitmap bitmap) {
mDefaultBitmap = bitmap;
}
public void setDefaultImage(int resId) {
mDefaultBitmap = BitmapFactory.decodeResource(mResources, resId);
}
protected ImageCache getImageCache() {
return mImageCache;
}
public void setImageFadeIn(boolean fadeIn) {
mFadeInBitmap = fadeIn;
}
public void setExitTasksEarly(boolean exitTasksEarly) {
mExitTasksEarly = exitTasksEarly;
setPauseWork(false);
}
public void setPauseWork(boolean pauseWork) {
synchronized (mPauseWorkLock) {
mPauseWork = pauseWork;
if (!mPauseWork) {
mPauseWorkLock.notifyAll();
}
}
}
public static void cancelWork(ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkingTask(imageView);
if (bitmapWorkerTask != null) {
bitmapWorkerTask.cancel(true);
}
}
public static boolean cancelPotentailWork(Object data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkingTask = getBitmapWorkingTask(imageView);
if(bitmapWorkingTask != null ) {
final Object bitmapData = bitmapWorkingTask.data;
if( bitmapData == null || !bitmapData.equals(data)) {
bitmapWorkingTask.cancel(true);
} else {
return false;
}
}
return true;
}
private static BitmapWorkerTask getBitmapWorkingTask(ImageView imageView) {
if( imageView != null) {
Drawable drawable = imageView.getDrawable();
if(drawable instanceof AsyncDrawable) {
return ((AsyncDrawable)drawable).getBitmapWorkingTask();
}
}
return null;
}
private void initDiskCache() {
new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
}
public void clearCache() {
new CacheAsyncTask().execute(MESSAGE_CLEAR);
}
public void closeCache() {
new CacheAsyncTask().execute(MESSAGE_CLOSE);
}
public void flushCache() {
new CacheAsyncTask().execute(MESSAGE_FLUSH);
}
protected void closeCacheInternal() {
if (mImageCache != null) {
mImageCache.close();
}
}
protected void flushCacheInternal() {
if (mImageCache != null) {
mImageCache.flush();
}
}
protected void initDiskCacheInternal() {
if (mImageCache != null) {
mImageCache.initDiskCache();
}
}
protected void clearCacheInternal() {
if (mImageCache != null) {
mImageCache.clearCache();
}
}
/**
* Subclasses should override this to define any processing or work that must happen to produce
* the final bitmap. This will be executed in a background thread and be long running. For
* example, you could resize a large bitmap here, or pull down an image from the network.
*
* @param data The data to identify which image to process, as provided by
* {@link ImageWorker#loadImage(Object, android.widget.ImageView)}
* @return The processed bitmap
*/
protected abstract Bitmap processBitmap(Object data);
private class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask task) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<ImageWorker.BitmapWorkerTask>(task);
}
public BitmapWorkerTask getBitmapWorkingTask() {
return bitmapWorkerTaskReference.get();
}
}
protected class CacheAsyncTask extends AsyncTask<Object, Void, Void> {
@Override
protected Void doInBackground(Object... params) {
int type = (Integer) params[0];
switch (type) {
case MESSAGE_CLEAR:
clearCacheInternal();
break;
case MESSAGE_INIT_DISK_CACHE:
initDiskCacheInternal();
break;
case MESSAGE_FLUSH:
flushCacheInternal();
break;
case MESSAGE_CLOSE:
closeCacheInternal();
break;
}
return null;
}
}
private class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {
private final WeakReference<ImageView> imageViewReference;
private Object data;
public BitmapWorkerTask(Object data, ImageView imageView) {
this.data = data;
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected BitmapDrawable doInBackground(Object... params) {
final String dataString = String.valueOf(data);
Bitmap bitmap = null;
BitmapDrawable drawable = null;
synchronized (mPauseWorkLock) {
while (mPauseWork && !isCancelled()) {
try {
mPauseWorkLock.wait();
} catch (InterruptedException e) {
}
}
}
// If the image cache is available and this task has not been cancelled by another
// thread and the ImageView that was originally bound to this task is still bound back
// to this task and our "exit early" flag is not set then try and fetch the bitmap from
// the cache
if (mImageCache != null && !isCancelled() && getAttachedImageView() != null
&& !mExitTasksEarly) {
bitmap = processBitmap(data);
}
// If the bitmap was processed and the image cache is available, then add the processed
// bitmap to the cache for future use. Note we don't check if the task was cancelled
// here, if it was, and the thread is still running, we may as well add the processed
// bitmap to our cache as it might be used again in the future
if (bitmap != null) {
if (Utils.hasHoneycomb()) {
drawable = new BitmapDrawable(mResources, bitmap);
} else {
drawable = new RecyclingBitmapDrawable(mResources, bitmap);
}
if (mImageCache != null) {
mImageCache.addBitmapToCache(dataString, drawable);
}
}
return drawable;
}
@Override
protected void onPostExecute(BitmapDrawable bitmapDrawable) {
if(isCancelled()) {
bitmapDrawable = null;
}
final ImageView imageView = getAttachedImageView();
if(imageViewReference != null && imageView != null) {
setImageDrawable(imageView, bitmapDrawable);
}
}
@Override
protected void onCancelled() {
super.onCancelled();
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
private ImageView getAttachedImageView() {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkingTask(imageView);
if (this == bitmapWorkerTask) {
return imageView;
}
return null;
}
}
public void setImageDrawable(ImageView imageView,
BitmapDrawable bitmapDrawable) {
if (mFadeInBitmap && mDefaultBitmap != null) {
final TransitionDrawable td = new TransitionDrawable(new Drawable[] {
new ColorDrawable(android.R.color.transparent),
bitmapDrawable
});
imageView.setBackgroundDrawable(new BitmapDrawable(mResources, mDefaultBitmap));
imageView.setImageDrawable(td);
td.startTransition(FADE_IN_TIME );
} else {
imageView.setImageDrawable(bitmapDrawable);
}
}
}