/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zhaojian.jolly.views;
import java.lang.ref.WeakReference;
import java.util.concurrent.Future;
import uk.co.senab.bitmapcache.BitmapLruCache;
import uk.co.senab.bitmapcache.CacheableBitmapWrapper;
import uk.co.senab.bitmapcache.CacheableImageView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import com.zhaojian.jolly.model.PhotoUpload;
import com.zhaojian.jolly.selectphotos.SelectPhotoApplication;
import com.zhaojian.jolly.tasks.PhotupThreadRunnable;
public class PhotupImageView extends CacheableImageView {
public static interface OnPhotoLoadListener {
void onPhotoLoadFinished(Bitmap bitmap);
}
static final class PhotoFilterRunnable extends PhotupThreadRunnable {
private final WeakReference<PhotupImageView> mImageView;
private final PhotoUpload mUpload;
private final boolean mFullSize;
private final BitmapLruCache mCache;
private final OnPhotoLoadListener mListener;
public PhotoFilterRunnable(PhotupImageView imageView, PhotoUpload upload,
BitmapLruCache cache,
final boolean fullSize, final OnPhotoLoadListener listener) {
mImageView = new WeakReference<PhotupImageView>(imageView);
mUpload = upload;
mFullSize = fullSize;
mCache = cache;
mListener = listener;
}
public void runImpl() {
final PhotupImageView imageView = mImageView.get();
if (null == imageView) {
return;
}
final Context context = imageView.getContext();
final Bitmap filteredBitmap;
final String key = mFullSize ? mUpload.getDisplayImageKey()
: mUpload.getThumbnailImageKey();
CacheableBitmapWrapper wrapper = mCache.get(key);
if (null == wrapper || !wrapper.hasValidBitmap()) {
Bitmap bitmap = mFullSize ? mUpload.getDisplayImage(context)
: mUpload.getThumbnailImage(context);
wrapper = new CacheableBitmapWrapper(key, bitmap);
wrapper.setBeingUsed(true);
mCache.put(wrapper);
} else {
wrapper.setBeingUsed(true);
}
// Don't process if we've been interrupted
if (!isInterrupted()) {
filteredBitmap = mUpload.processBitmap(wrapper.getBitmap(), mFullSize, false);
} else {
filteredBitmap = null;
}
// Make sure we release the original bitmap
wrapper.setBeingUsed(false);
// If we haven't been interrupted, update the view
if (!isInterrupted()) {
imageView.post(new Runnable() {
public void run() {
imageView.setImageBitmap(filteredBitmap);
if (null != mListener) {
mListener.onPhotoLoadFinished(filteredBitmap);
}
}
});
}
}
}
static final class PhotoLoadRunnable extends PhotupThreadRunnable {
private final WeakReference<PhotupImageView> mImageView;
private final PhotoUpload mUpload;
private final boolean mFullSize;
private final BitmapLruCache mCache;
private final OnPhotoLoadListener mListener;
public PhotoLoadRunnable(PhotupImageView imageView, PhotoUpload upload,
BitmapLruCache cache,
final boolean fullSize, final OnPhotoLoadListener listener) {
mImageView = new WeakReference<PhotupImageView>(imageView);
mUpload = upload;
mFullSize = fullSize;
mCache = cache;
mListener = listener;
}
public void runImpl() {
final PhotupImageView imageView = mImageView.get();
if (null == imageView) {
return;
}
final Context context = imageView.getContext();
final CacheableBitmapWrapper wrapper;
final Bitmap bitmap = mFullSize ? mUpload.getDisplayImage(context)
: mUpload.getThumbnailImage(context);
if (null != bitmap) {
final String key = mFullSize ? mUpload.getDisplayImageKey()
: mUpload.getThumbnailImageKey();
wrapper = new CacheableBitmapWrapper(key, bitmap);
} else {
wrapper = null;
}
// If we're interrupted, just update the cache and return
if (isInterrupted()) {
mCache.put(wrapper);
return;
}
// If we're still running, update the Views
if (null != wrapper) {
imageView.post(new Runnable() {
public void run() {
imageView.setImageCachedBitmap(wrapper);
mCache.put(wrapper);
if (null != mListener) {
mListener.onPhotoLoadFinished(wrapper.getBitmap());
}
}
});
}
}
}
static final int FACE_DETECTION_DELAY = 800;
private boolean mFadeInDrawables = false;
private Drawable mFadeFromDrawable;
private int mFadeDuration;
private Runnable mRequestFaceDetectionRunnable;
private Future<?> mCurrentRunnable;
public PhotupImageView(Context context) {
super(context);
}
public PhotupImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void cancelRequest() {
if (null != mCurrentRunnable) {
mCurrentRunnable.cancel(true);
mCurrentRunnable = null;
}
}
public void clearFaceDetection() {
if (null != mRequestFaceDetectionRunnable) {
removeCallbacks(mRequestFaceDetectionRunnable);
mRequestFaceDetectionRunnable = null;
}
}
public Bitmap getCurrentBitmap() {
Drawable d = getDrawable();
if (d instanceof BitmapDrawable) {
return ((BitmapDrawable) d).getBitmap();
}
return null;
}
public void recycleBitmap() {
Bitmap currentBitmap = getCurrentBitmap();
if (null != currentBitmap) {
setImageDrawable(null);
currentBitmap.recycle();
}
}
public void requestFullSize(final PhotoUpload upload, final boolean honourFilter,
final boolean clearDrawableOnLoad, final OnPhotoLoadListener listener) {
resetForRequest(clearDrawableOnLoad);
if (upload.requiresProcessing(true) && honourFilter) {
requestFiltered(upload, true, listener);
} else {
// Show thumbnail if it's in the cache
BitmapLruCache cache = SelectPhotoApplication.getInstanse().getImageCache();
CacheableBitmapWrapper thumbWrapper = cache.get(upload.getThumbnailImageKey());
if (null != thumbWrapper && thumbWrapper.hasValidBitmap()) {
setImageCachedBitmap(thumbWrapper);
} else {
setImageDrawable(null);
}
requestImage(upload, true, listener);
}
}
private void requestFiltered(final PhotoUpload upload, boolean fullSize,
final OnPhotoLoadListener listener) {
mCurrentRunnable = SelectPhotoApplication.getInstanse().getPhotoFilterThreadExecutorService().submit(
new PhotoFilterRunnable(this, upload, SelectPhotoApplication.getInstanse().getImageCache(), fullSize, listener));
}
public void requestFullSize(final PhotoUpload upload, final boolean honourFilter,
final OnPhotoLoadListener listener) {
requestFullSize(upload, honourFilter, true, listener);
}
public void requestThumbnail(final PhotoUpload upload, final boolean honourFilter) {
resetForRequest(true);
if (upload.requiresProcessing(false) && honourFilter) {
requestFiltered(upload, false, null);
} else {
requestImage(upload, false, null);
}
}
public void setFadeInDrawables(boolean fadeIn) {
mFadeInDrawables = fadeIn;
if (fadeIn && null == mFadeFromDrawable) {
mFadeFromDrawable = new ColorDrawable(Color.TRANSPARENT);
mFadeDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
}
}
@Override
public void setImageDrawable(Drawable drawable) {
if (mFadeInDrawables && null != drawable) {
TransitionDrawable newDrawable = new TransitionDrawable(
new Drawable[]{mFadeFromDrawable, drawable});
super.setImageDrawable(newDrawable);
newDrawable.startTransition(mFadeDuration);
} else {
super.setImageDrawable(drawable);
}
}
private void requestImage(final PhotoUpload upload, final boolean fullSize,
final OnPhotoLoadListener listener) {
final String key = fullSize ? upload.getDisplayImageKey() : upload.getThumbnailImageKey();
BitmapLruCache cache = SelectPhotoApplication.getInstanse().getImageCache();
final CacheableBitmapWrapper cached = cache.get(key);
if (null != cached && cached.hasValidBitmap()) {
setImageCachedBitmap(cached);
if (null != listener) {
listener.onPhotoLoadFinished(cached.getBitmap());
}
} else {
// Means we have an object with an invalid bitmap so remove it
if (null != cached) {
cache.remove(key);
}
mCurrentRunnable = SelectPhotoApplication.getInstanse().getMultiThreadExecutorService().submit(
new PhotoLoadRunnable(this, upload, cache, fullSize, listener));
}
}
private void resetForRequest(final boolean clearDrawable) {
cancelRequest();
// Clear currently display bitmap
if (clearDrawable) {
setImageDrawable(null);
}
}
}