package com.duckduckgo.mobile.android.download;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;
import android.graphics.Bitmap;
import android.os.Handler;
//TODO: Do we want to add any file caching for any objects?
public class ImageCache {
private static final String TAG = "ImageCache";
private static final int CACHE_CAPACITY = 6; //How many items we can have that the GC won't touch (we still purge it though)
private static final int PURGE_DELAY = 60000; //milliseconds before we purge all items (60 seconds)
private FileCache fileCache = null;
public ImageCache(FileCache fileCache) {
this.fileCache = fileCache;
}
//The hard cache will hold references that we don't want to lose (in our case the 50 most recent)
@SuppressWarnings("serial")
private final HashMap<String, Bitmap> hardBitmapCache = new LinkedHashMap<String, Bitmap>(CACHE_CAPACITY / 2, 0.75f, true) {
@Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> oldest) {
//If we have too many objects, remove the oldest one and place it in soft reference
//The garbage collector will periodically remove soft references...
if (size() > CACHE_CAPACITY) {
softBitmapCache.put(oldest.getKey(), new SoftReference<Bitmap>(oldest.getValue()));
return true;
} else {
return false;
}
}
};
//The soft cache will hold older references that we don't care as much if the GC cleans up
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> softBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(CACHE_CAPACITY / 2);
private final Handler purgeHandler = new Handler();
private final Runnable doPurge = new Runnable() {
public void run() {
clearCache();
}
};
private String getCleanFileName(String url){
String preUrl = url.substring(url.lastIndexOf("/")+1);
int idxQues = preUrl.lastIndexOf("?");
if(idxQues != -1){
preUrl = preUrl.substring(0,preUrl.lastIndexOf("?"));
}
return preUrl;
}
public void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized(hardBitmapCache) {
hardBitmapCache.put(url, bitmap);
}
//Save the bitmap to the file cache as well
if (fileCache != null) {
fileCache.saveBitmapAsFile(getCleanFileName(url), bitmap);
}
}
}
public Bitmap getBitmapFromCache(String url, boolean onlyUseMemCache) {
resetPurgeTimer();
if (url == null) return null;
synchronized(hardBitmapCache) {
// get() is enough for access-order
final Bitmap bitmap = hardBitmapCache.get(url);
if (bitmap != null) {
return bitmap;
}
}
SoftReference<Bitmap> bitmapReference = softBitmapCache.get(url);
if (bitmapReference != null) {
final Bitmap bitmap = bitmapReference.get();
if (bitmap != null) {
return bitmap;
} else {
//Remove the url key since the reference no longer exists
softBitmapCache.remove(url);
}
}
if(onlyUseMemCache){
return null;
}
if (fileCache != null) {
Bitmap bitmap = fileCache.getBitmapFromImageFile(getCleanFileName(url));
if (bitmap != null) {
synchronized(hardBitmapCache) {
hardBitmapCache.put(url, bitmap);
}
}
return bitmap;
}
return null;
}
private void resetPurgeTimer() {
purgeHandler.removeCallbacks(doPurge);
purgeHandler.postDelayed(doPurge, PURGE_DELAY);
}
private void clearCache() {
hardBitmapCache.clear();
softBitmapCache.clear();
}
/**
* Purge in-memory cache resources
*/
public void purge() {
doPurge.run();
}
public void setFileCache(FileCache fileCache) {
this.fileCache = fileCache;
}
}