/**
* galaxy inc.
* meetup client for android
*/
package com.galaxy.meetup.client.android.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.Log;
import com.galaxy.meetup.client.android.EsApplication;
import com.galaxy.meetup.client.android.R;
import com.galaxy.meetup.client.android.content.AvatarImageRequest;
import com.galaxy.meetup.client.android.content.AvatarRequest;
import com.galaxy.meetup.client.android.content.EsAvatarData;
import com.galaxy.meetup.client.android.content.MediaImageRequest;
import com.galaxy.meetup.client.android.content.cache.CachedImageRequest;
import com.galaxy.meetup.client.android.content.cache.EsMediaCache;
import com.galaxy.meetup.client.android.content.cache.ImageRequest;
import com.galaxy.meetup.client.util.EsLog;
import com.galaxy.meetup.client.util.GifDrawable;
import com.galaxy.meetup.client.util.ImageLoadingMetrics;
import com.galaxy.meetup.client.util.ImageUtils;
public class ImageCache implements Callback {
private static final byte EMPTY_ARRAY[] = new byte[0];
private static final Object FAILED_IMAGE = new Object();
private static Set<OnAvatarChangeListener> mAvatarListeners = new HashSet<OnAvatarChangeListener>();
private static Set<OnMediaImageChangeListener> mMediaImageListeners = new HashSet<OnMediaImageChangeListener>();
private static Set mRequestCompleteListeners = new HashSet();
private static ImageCache sInstance;
private static int sMediumAvatarEstimatedSize;
private static int sSmallAvatarEstimatedSize;
private static int sTinyAvatarEstimatedSize;
private int mBackgroundThreadBitmapCount;
private final Context mContext;
private final LruCache mImageCache;
private final LruCache<ImageRequest, ImageHolder> mImageHolderCache;
private final int mImageHolderCacheRedZoneBytes;
private final ArrayList mImageHolderQueue = new ArrayList();
private LoaderThread mLoaderThread;
private boolean mLoadingRequested;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), this);
private boolean mPaused;
private final ConcurrentHashMap<ImageConsumer, ImageRequest> mPendingRequests = new ConcurrentHashMap<ImageConsumer, ImageRequest>();
// ===========================================================================
// Constructor
// ===========================================================================
private ImageCache(Context context) {
mContext = context;
Resources resources = context.getApplicationContext().getResources();
int i;
int j;
int k;
if (EsApplication.sMemoryClass >= 48)
i = Math.max(resources.getInteger(R.integer.config_image_cache_max_bytes_decoded_large),
1024 * (1024 * (EsApplication.sMemoryClass / 4)));
else
i = resources.getInteger(R.integer.config_image_cache_max_bytes_decoded_small);
mImageCache = new LruCache(i) {
protected final int sizeOf(Object obj, Object obj1) {
int l;
if (obj1 instanceof Bitmap) {
Bitmap bitmap = (Bitmap) obj1;
l = bitmap.getRowBytes() * bitmap.getHeight();
} else {
l = 0;
}
return l;
}
};
if (EsApplication.sMemoryClass >= 48)
j = 1024 * (1024 * (EsApplication.sMemoryClass / 4));
else
j = 0;
k = Math.max(j, resources.getInteger(R.integer.config_image_cache_max_bytes_encoded));
mImageHolderCache = new LruCache<ImageRequest, ImageHolder>(k) {
protected final int sizeOf(ImageRequest key, ImageHolder value) {
ImageHolder imageholder = (ImageHolder) value;
int l;
if (imageholder.bytes != null)
l = imageholder.bytes.length;
else
l = 0;
return l;
}
};
mImageHolderCacheRedZoneBytes = (int) (0.90000000000000002D * (double) k);
if (sTinyAvatarEstimatedSize == 0) {
sTinyAvatarEstimatedSize = (int) (0.3F * (float) EsAvatarData.getTinyAvatarSize(context) * (float) EsAvatarData
.getTinyAvatarSize(context));
sSmallAvatarEstimatedSize = (int) (0.3F * (float) EsAvatarData.getSmallAvatarSize(context) * (float) EsAvatarData
.getSmallAvatarSize(context));
sMediumAvatarEstimatedSize = (int) (0.3F * (float) EsAvatarData.getMediumAvatarSize(context) * (float) EsAvatarData
.getMediumAvatarSize(context));
}
}
@Override
public boolean handleMessage(Message message) {
boolean result = false;
switch (message.what) {
case 1:
mLoadingRequested = false;
if (!mPaused) {
ensureLoaderThread();
mLoaderThread.requestLoading();
}
result = true;
break;
case 2:
if (!mPaused) {
Iterator iterator3 = mPendingRequests.keySet().iterator();
do {
if (!iterator3.hasNext())
break;
ImageConsumer imageconsumer = (ImageConsumer) iterator3.next();
ImageRequest imagerequest1 = (ImageRequest) mPendingRequests.get(imageconsumer);
if (imagerequest1 != null && loadCachedImage(imageconsumer, imagerequest1, false))
iterator3.remove();
} while (true);
clearTemporaryImageReferences();
mBackgroundThreadBitmapCount = 0;
if (!mPendingRequests.isEmpty())
requestLoading();
}
result = true;
break;
case 3:
String s = (String) message.obj;
evictImage(new AvatarRequest(s, 0));
evictImage(new AvatarRequest(s, 0, true));
evictImage(new AvatarRequest(s, 1));
evictImage(new AvatarRequest(s, 1, true));
evictImage(new AvatarRequest(s, 2));
evictImage(new AvatarRequest(s, 2, true));
for (Iterator iterator2 = mAvatarListeners.iterator(); iterator2.hasNext(); ((OnAvatarChangeListener) iterator2
.next()).onAvatarChanged(s))
;
result = true;
break;
case 4:
MediaImageChangeNotification mediaimagechangenotification = (MediaImageChangeNotification) message.obj;
Iterator iterator = mImageHolderCache.snapshot().keySet().iterator();
do {
if (!iterator.hasNext())
break;
ImageRequest imagerequest = (ImageRequest) iterator.next();
if (!imagerequest.equals(mediaimagechangenotification.request)
&& (imagerequest instanceof MediaImageRequest)
&& MediaImageRequest.areCanonicallyEqual((MediaImageRequest) imagerequest,
mediaimagechangenotification.request))
evictImage(imagerequest);
} while (true);
for (Iterator iterator1 = mMediaImageListeners.iterator(); iterator1.hasNext(); ((OnMediaImageChangeListener) iterator1
.next()).onMediaImageChanged(mediaimagechangenotification.request.getUrl()))
;
result = true;
break;
default:
break;
}
return result;
}
public static synchronized ImageCache getInstance(Context context) {
if (sInstance == null)
sInstance = new ImageCache(context.getApplicationContext());
return sInstance;
}
public final void loadImage(ImageConsumer imageconsumer, ImageRequest imagerequest) {
loadImage(imageconsumer, imagerequest, true);
}
private void loadImage(ImageConsumer imageconsumer, ImageRequest imagerequest, boolean flag) {
if (ImageLoadingMetrics.areImageLoadingMetricsEnabled())
ImageLoadingMetrics.recordLoadImageRequest(imagerequest.getUriForLogging());
if (imagerequest.isEmpty()) {
imageconsumer.setBitmap(null, false);
notifyRequestComplete(imagerequest);
return;
}
mPendingRequests.remove(imageconsumer);
if (loadCachedImage(imageconsumer, imagerequest, flag)) {
mPendingRequests.remove(imageconsumer);
} else {
mPendingRequests.put(imageconsumer, imagerequest);
if (!mPaused)
requestLoading();
}
}
private boolean loadCachedImage(ImageConsumer imageconsumer, ImageRequest imagerequest, boolean flag) {
boolean flag1 = false;
ImageHolder imageholder = (ImageHolder) mImageHolderCache.get(imagerequest);
if (imageholder == null || !imageholder.fresh || !imageholder.complete || imageholder.image != null
|| imageholder.bytes == null || imageholder.bytes.length <= 4000
|| mImageCache.get(imagerequest) != null) {
if (imageholder == null) {
flag1 = false;
if (flag) {
imageconsumer.setBitmap(null, true);
flag1 = false;
}
return flag1;
}
mImageHolderCache.put(imagerequest, imageholder);
if (mImageHolderCache.get(imagerequest) == null) {
imageholder = new ImageHolder(null, true);
mImageHolderCache.put(imagerequest, imageholder);
}
if (imageholder.bytes == null) {
if (imageholder.complete) {
imageconsumer.setBitmap(null, false);
if (ImageLoadingMetrics.areImageLoadingMetricsEnabled())
ImageLoadingMetrics.recordImageDelivered(imagerequest.getUriForLogging(), 0, 0);
notifyRequestComplete(imagerequest);
} else {
imageconsumer.setBitmap(null, true);
}
flag1 = imageholder.fresh;
return flag1;
}
decodeImage(imagerequest, imageholder);
Object obj = imageholder.image;
if (obj instanceof Bitmap) {
imageconsumer.setBitmap((Bitmap) obj, false);
if (ImageLoadingMetrics.areImageLoadingMetricsEnabled()) {
Bitmap bitmap = (Bitmap) obj;
String s = imagerequest.getUriForLogging();
int i;
if (imageholder.bytes == null)
i = 0;
else
i = imageholder.bytes.length;
ImageLoadingMetrics.recordImageDelivered(s, i, bitmap.getByteCount());
}
} else if ((obj instanceof Drawable) && (imageconsumer instanceof DrawableConsumer))
((DrawableConsumer) imageconsumer).setDrawable((Drawable) obj, false);
else if (obj instanceof GifDrawable) {
imageconsumer.setBitmap(null, false);
} else {
if (obj != FAILED_IMAGE)
throw new UnsupportedOperationException((new StringBuilder("Cannot handle drawables of type "))
.append(obj.getClass()).toString());
imageconsumer.setBitmap(null, false);
}
notifyRequestComplete(imagerequest);
imageholder.image = null;
flag1 = imageholder.fresh;
return flag1;
} else {
imageholder.decodeInBackground = true;
return flag1;
}
}
public final void notifyAvatarChange(String s) {
if (s != null) {
ensureLoaderThread();
mLoaderThread.notifyAvatarChange(s);
}
}
public static void registerAvatarChangeListener(OnAvatarChangeListener onavatarchangelistener) {
mAvatarListeners.add(onavatarchangelistener);
}
public static void registerMediaImageChangeListener(OnMediaImageChangeListener onmediaimagechangelistener) {
mMediaImageListeners.add(onmediaimagechangelistener);
}
private void requestLoading() {
if (!mLoadingRequested) {
mLoadingRequested = true;
mMainThreadHandler.sendEmptyMessage(1);
}
}
public static void unregisterAvatarChangeListener(OnAvatarChangeListener onavatarchangelistener) {
mAvatarListeners.remove(onavatarchangelistener);
}
public static void unregisterMediaImageChangeListener(OnMediaImageChangeListener onmediaimagechangelistener) {
mMediaImageListeners.remove(onmediaimagechangelistener);
}
public final void cancel(ImageConsumer imageconsumer) {
mPendingRequests.remove(imageconsumer);
}
public final void clear() {
mImageHolderCache.evictAll();
mImageCache.evictAll();
mPendingRequests.clear();
}
public final void clearFailedRequests() {
Iterator<ImageRequest> iterator = mImageHolderCache.snapshot().keySet().iterator();
do {
if (!iterator.hasNext())
break;
ImageRequest imagerequest = iterator.next();
ImageHolder imageholder = mImageHolderCache.get(imagerequest);
if (imageholder != null && imageholder.fresh && imageholder.bytes == null)
imageholder.fresh = false;
} while (true);
}
public final void notifyMediaImageChange(CachedImageRequest cachedimagerequest, byte abyte0[]) {
ensureLoaderThread();
if (cachedimagerequest instanceof MediaImageRequest) {
MediaImageChangeNotification mediaimagechangenotification = new MediaImageChangeNotification((byte) 0);
mediaimagechangenotification.request = (MediaImageRequest) cachedimagerequest;
mediaimagechangenotification.imageBytes = abyte0;
mLoaderThread.notifyMediaImageChange(mediaimagechangenotification);
} else if (cachedimagerequest instanceof AvatarImageRequest) {
ensureLoaderThread();
mLoaderThread.notifyAvatarChange(((AvatarImageRequest) cachedimagerequest).getGaiaId());
}
}
public final void pause() {
mPaused = true;
}
public final void refreshImage(ImageConsumer imageconsumer, ImageRequest imagerequest) {
loadImage(imageconsumer, imagerequest, false);
}
public final void resume() {
mPaused = false;
if (!mPendingRequests.isEmpty())
requestLoading();
}
private static void notifyRequestComplete(ImageRequest imagerequest) {
for (Iterator iterator = mRequestCompleteListeners.iterator(); iterator.hasNext(); iterator.next())
;
}
private void evictImage(ImageRequest imagerequest) {
mImageCache.remove(imagerequest);
ImageHolder imageholder = (ImageHolder) mImageHolderCache.get(imagerequest);
if (imageholder != null)
imageholder.fresh = false;
}
private void ensureLoaderThread() {
if (mLoaderThread == null) {
mContext.getContentResolver();
mLoaderThread = new LoaderThread();
mLoaderThread.start();
}
}
private void decodeImage(ImageRequest imagerequest, ImageHolder imageholder) {
if (null == imageholder.image) {
byte abyte0[] = imageholder.bytes;
if (abyte0 != null && abyte0.length != 0) {
imageholder.image = mImageCache.get(imagerequest);
if (imageholder.image == null) {
imageholder.image = ImageUtils.decodeMedia(abyte0);
if (imageholder.image == null)
imageholder.image = FAILED_IMAGE;
mImageCache.put(imagerequest, imageholder.image);
if (mImageCache.get(imagerequest) == null) {
imageholder.image = FAILED_IMAGE;
mImageCache.put(imagerequest, FAILED_IMAGE);
}
}
}
}
}
private void clearTemporaryImageReferences() {
synchronized (mImageHolderQueue) {
for (Iterator iterator = mImageHolderQueue.iterator(); iterator.hasNext();)
((ImageHolder) iterator.next()).image = null;
mImageHolderQueue.clear();
}
}
static void access$400(ImageCache imagecache, ImageRequest imagerequest, byte abyte0[], boolean flag, boolean flag1) {
ImageHolder imageholder = new ImageHolder(abyte0, flag);
imageholder.fresh = true;
if (flag && !flag1 && imagecache.mPendingRequests.containsValue(imagerequest)
&& imagecache.mBackgroundThreadBitmapCount < 2) {
imagecache.mBackgroundThreadBitmapCount = 1 + imagecache.mBackgroundThreadBitmapCount;
imagecache.decodeImage(imagerequest, imageholder);
}
imagecache.mImageHolderCache.put(imagerequest, imageholder);
if (imagecache.mImageHolderCache.get(imagerequest) == null) {
imageholder = new ImageHolder(null, true);
imagecache.mImageHolderCache.put(imagerequest, imageholder);
}
synchronized (imagecache.mImageHolderQueue) {
imagecache.mImageHolderQueue.add(imageholder);
}
return;
}
// ===========================================================================
// Inner class
// ===========================================================================
private static class ImageHolder {
final byte bytes[];
final boolean complete;
boolean decodeInBackground;
volatile boolean fresh;
Object image;
public ImageHolder(byte[] bytes, boolean complete) {
this.bytes = bytes;
this.fresh = true;
this.complete = complete;
}
}
public static interface ImageConsumer {
public abstract void setBitmap(Bitmap bitmap, boolean flag);
}
public static interface DrawableConsumer extends ImageConsumer {
public abstract void setDrawable(Drawable drawable, boolean flag);
}
private static final class MediaImageChangeNotification {
byte imageBytes[];
MediaImageRequest request;
private MediaImageChangeNotification() {
}
MediaImageChangeNotification(byte byte0) {
this();
}
}
public static interface OnAvatarChangeListener {
public abstract void onAvatarChanged(String s);
}
public static interface OnMediaImageChangeListener {
public abstract void onMediaImageChanged(String s);
}
private final class LoaderThread extends HandlerThread implements Handler.Callback {
private Handler mLoaderThreadHandler;
private List mPreloadRequests;
private int mPreloadStatus;
private final Set mRequests = new HashSet();
public LoaderThread() {
super("ImageCache", 1);
mPreloadRequests = new ArrayList();
mPreloadStatus = 0;
}
private void continuePreloading() {
if (mPreloadStatus != 2) {
ensureHandler();
if (!mLoaderThreadHandler.hasMessages(2))
mLoaderThreadHandler.sendEmptyMessageDelayed(1, 50L);
}
}
private void ensureHandler() {
if (mLoaderThreadHandler == null)
mLoaderThreadHandler = new Handler(getLooper(), this);
}
private void loadImagesFromDatabase(boolean flag) {
if (mRequests.size() != 0) {
if (!flag && mPreloadStatus == 1) {
mPreloadRequests.removeAll(mRequests);
if (mPreloadRequests.isEmpty())
mPreloadStatus = 2;
}
List arraylist = null;
List arraylist1 = null;
List arraylist2 = null;
for (Iterator iterator = mRequests.iterator(); iterator.hasNext();) {
ImageRequest imagerequest = (ImageRequest) iterator.next();
if (imagerequest instanceof AvatarRequest) {
if (arraylist == null)
arraylist = new ArrayList();
arraylist.add((AvatarRequest) imagerequest);
} else if (imagerequest instanceof MediaImageRequest) {
if (arraylist1 == null)
arraylist1 = new ArrayList();
arraylist1.add((MediaImageRequest) imagerequest);
} else {
if (arraylist2 == null)
arraylist2 = new ArrayList();
arraylist2.add(imagerequest);
}
}
if (arraylist1 != null) {
CachedImageRequest cachedimagerequest;
for (Iterator iterator3 = EsMediaCache.loadMedia(mContext, arraylist1).entrySet().iterator(); iterator3
.hasNext(); mRequests.remove(cachedimagerequest)) {
java.util.Map.Entry entry1 = (java.util.Map.Entry) iterator3.next();
cachedimagerequest = (CachedImageRequest) entry1.getKey();
access$400(ImageCache.this, cachedimagerequest, (byte[]) entry1.getValue(), true, flag);
mRequests.remove(cachedimagerequest);
}
}
if (arraylist != null) {
AvatarRequest avatarrequest;
for (Iterator iterator2 = EsAvatarData.loadAvatars(mContext, arraylist).entrySet().iterator(); iterator2
.hasNext(); mRequests.remove(avatarrequest)) {
java.util.Map.Entry entry = (java.util.Map.Entry) iterator2.next();
avatarrequest = (AvatarRequest) entry.getKey();
access$400(ImageCache.this, avatarrequest, (byte[]) entry.getValue(), true, flag);
mRequests.remove(avatarrequest);
}
}
ImageRequest imagerequest;
for (Iterator iterator1 = mRequests.iterator(); iterator1.hasNext();) {
imagerequest = (ImageRequest) iterator1.next();
access$400(ImageCache.this, imagerequest, null, false, flag);
}
mMainThreadHandler.sendEmptyMessage(2);
}
}
private void preloadAvatarsInBackground() {
if (mPreloadStatus != 2)
if (mPreloadStatus == 0) {
if (mPreloadRequests.isEmpty())
mPreloadStatus = 2;
else
mPreloadStatus = 1;
continuePreloading();
} else if (mImageHolderCache.size() > mImageHolderCacheRedZoneBytes) {
mPreloadStatus = 2;
} else {
mRequests.clear();
int i = 0;
int j = mPreloadRequests.size();
do {
if (j <= 0 || mRequests.size() >= 25)
break;
j--;
AvatarRequest avatarrequest = (AvatarRequest) mPreloadRequests.get(j);
mPreloadRequests.remove(j);
if (mImageHolderCache.get(avatarrequest) == null) {
mRequests.add(avatarrequest);
i++;
}
} while (true);
loadImagesFromDatabase(true);
if (j == 0)
mPreloadStatus = 2;
if (EsLog.isLoggable("ImageCache", 4))
Log.v("ImageCache",
(new StringBuilder("Preloaded ")).append(i).append(" avatars. Cache size (bytes): ")
.append(mImageHolderCache.size()).toString());
continuePreloading();
}
}
public final boolean handleMessage(Message message) {
// TODO
return false;
}
public final void notifyAvatarChange(String s) {
ensureHandler();
Message message = mLoaderThreadHandler.obtainMessage(3, s);
mLoaderThreadHandler.sendMessage(message);
}
public final void notifyMediaImageChange(MediaImageChangeNotification mediaimagechangenotification) {
ensureHandler();
Message message = mLoaderThreadHandler.obtainMessage(4, mediaimagechangenotification);
mLoaderThreadHandler.sendMessage(message);
}
public final void requestLoading() {
ensureHandler();
mLoaderThreadHandler.removeMessages(1);
mLoaderThreadHandler.sendEmptyMessage(2);
}
}
}